• Stars
    star
    308
  • Rank 135,712 (Top 3 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created about 6 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A lightweight form validation script that augments native HTML5 form validation elements and attributes.

Bouncer.js Build Status

A lightweight form validation script that augments native HTML5 form validation elements and attributes.

View the Demo on CodePen →

Getting Started | Form Validation Attributes | Error Styling | Error Types | Custom Validations | Error Location | API | Browser Compatibility | License |

Features:

  • Fields validate on blur (as the user moves out of them), which data shows is the best time for cognitive load.
  • The entire form is validated on submit.
  • Fields with errors are revalidated as the user types. Errors are removed the instant the field is valid.
  • Supports custom validation patterns and error messages.

Want to learn how to write your own vanilla JS plugins? Check out my Vanilla JS Pocket Guides or join the Vanilla JS Academy and level-up as a web developer. 🚀


Getting Started

Compiled and production-ready code can be found in the dist directory. The src directory contains development code.

1. Include Bouncer on your site.

There are two versions of Bouncer: the standalone version, and one that comes preloaded with polyfills for closest(), matches(), classList, and CustomEvent(), which are only supported in newer browsers.

If you're including your own polyfills or don't want to enable this feature for older browsers, use the standalone version. Otherwise, use the version with polyfills.

Direct Download

You can download the files directly from GitHub.

<script src="path/to/bouncer.polyfills.min.js"></script>

CDN

You can also use the jsDelivr CDN. I recommend linking to a specific version number or version range to prevent major updates from breaking your site. Smooth Scroll uses semantic versioning.

<!-- Always get the latest version -->
<!-- Not recommended for production sites! -->
<script src="https://cdn.jsdelivr.net/gh/cferdinandi/bouncer/dist/bouncer.polyfills.min.js"></script>

<!-- Get minor updates and patch fixes within a major version -->
<script src="https://cdn.jsdelivr.net/gh/cferdinandi/bouncer@1/dist/bouncer.polyfills.min.js"></script>

<!-- Get patch fixes within a minor version -->
<script src="https://cdn.jsdelivr.net/gh/cferdinandi/[email protected]/dist/bouncer.polyfills.min.js"></script>

<!-- Get a specific version -->
<script src="https://cdn.jsdelivr.net/gh/cferdinandi/[email protected]/dist/bouncer.polyfills.min.js"></script>

NPM

You can also use NPM (or your favorite package manager).

npm install formbouncerjs

2. Add browser-native form validation attributes to your markup.

No special markup needed—just browser-native validation attributes (like required) and input types (like email or number).

<form>
	<label for="email">Your email address</label>
	<input type="email" name="email" id="email">
	<button>Submit</button>
</form>

3. Initialize Bouncer.

In the footer of your page, after the content, initialize Bouncer by passing in a selector for the forms that should be validated.

If the form has errors, submission will get blocked until they're corrected. Otherwise, it will submit as normal.

And that's it, you're done. Nice work!

<script>
	var validate = new Bouncer('form');
</script>

Form Validation Attributes

Modern browsers provide built-in form validation.

Bouncer hooks into those native attributes, suppressing the native validation and running its own. If Bouncer fails to load or run, the browser-native validation will run in its place.

Required fields

Add the required attribute to any field that must be filled out or selected.

<input type="text" name="first-name" required>

Special Input Types

You can use special type attribute values to indicate specific types of data that should be captured.

<!-- Must be a valid email address -->
<input type="email" name="email">

<!-- Must be a valid URL -->
<input type="url" name="website">

<!-- Must be a number -->
<input type="number" name="age">

<!-- Must be a date in YYYY-MM-DD format (many browsers include a native date picker for this) -->
<input type="date" name="dob">

<!-- Must be a time in 24-hour format (many browsers include a native date picker for this) -->
<input type="time" name="time">

<!-- Must be a month/year in YYYY-MM format (many browsers include a native date picker for this) -->
<input type="month" name="birthday">

<!-- Must be a color in #rrggbb format (many browsers include a native color picker for this) -->
<input type="color" name="color">

Min and Max Values

For numbers that should not go below or exceed a certain value, you can use the min and max attributes, respectively.

<!-- Cannot exceed 72 -->
<input type="number" max="72" name="answer">

<!-- Cannot be below 37 -->
<input type="number" min="37" name="answer">

<!-- They can also be combined -->
<input type="number" min="37" max="72" name="answer">

Min and Max Length

For inputs that should not be shorter or longer than a certain number of characters, you can use the minlength and maxlength attributes, respectively.

<!-- Cannot be shorter than 12 characters -->
<input type="password" minlength="12" name="password">

<!-- Cannot be longer than 24 characters -->
<input type="text" maxlength="24" name="first-name">

<!-- They can also be combined -->
<input type="text" minlength="7" maxlength="24" name="favorite-pixar-character">

Custom Validation Patterns

You can use your own validation pattern for a field with the pattern attribute. This uses regular expressions.

<!-- Phone number be in 555-555-5555 format -->
<input type="text" name="tel" pattern="\d{3}[\-]\d{3}[\-]\d{4}">

Custom Pattern Mismatch Error Messages

Show custom errors for pattern mismatches by adding the [data-bouncer-message] attribute to the field.

<!-- Phone number be in 555-555-5555 format -->
<input type="text" name="tel" pattern="\d{3}[\-]\d{3}[\-]\d{4}" data-bouncer-message="Please use the following format: 555-555-5555">

Error Styling

Bouncer does not come pre-packaged with any styles for fields with errors or error messages. Use the .error class to style fields, and the .error-message class to style error messages.

Need a starting point? Here's some really lightweight CSS you can use.

/**
 * Form Validation Errors
 */
.error {
	border-color: red;
}

.error-message {
	color: red;
	font-style: italic;
	margin-bottom: 1em;
}

Error Types

Bouncer captures four different types of field errors:

  • missingValue errors occur when a required field has no value (or for checkboxes and radio buttons, nothing is selected).
  • patternMismatch errors occur when a value doesn't match the expected pattern for a particular input type, or a pattern provided by the pattern attribute.
  • outOfRange errors occur when a number is above or below the min or max values.
  • wrongLength errors occur when the input is longer or shorter than the minlength and maxlength values.

The patterns and messages associated with these types of errors can be customized.

Custom Validation Types

You can add custom validation types to Bouncer beyond the four standard validations.

You can see this feature in action with the Confirm Password field on the demo page, and view examples of custom validations in the Cookbook (coming soon).

Adding custom validations

Pass in a customValidations object as an option when instantiating a new Bouncer instance. Each property in the object is a new validation type. Each value should be a function that accepts two arguments: the field being validated and the settings for the current instantiation.

The function should check if the field has an error. Return true if there's an error, and false when there's not.

var validate = new Bouncer('form', {
	customValidations: {
		isHello: function (field) {

			// Return false because there is NO error
			if (field.value === 'hello') return false;

			// Return true when there is
			return true;

		}
	}
});

Creating custom validation error messages

Add an error message for the custom validation by including the messages object in your options.

The key should be the same as your custom validation. It's value can be a string, or a function that returns a string. Message functions can accept two arguments: the field being validated and the settings for the current instantiation.

var validate = new Bouncer('form', {
	customValidations: {
		isHello: function (field) {

			// Return false because there is NO error
			if (field.value === 'hello') return false;

			// Return true when there is
			return true;

		}
	},
	messages: {
		// As a string
		isHello: 'This field should have a value of "hello"',

		// As a function
		isHello: function () {
			return 'This field should have a value of "hello"';
		}
	}
});

Error Message Location

By default, bouncer will render error messages after the invalid field (or the label for it, if the field is a radio or checkbox).

You can optionally render error messages before the field by setting the messageAfterField option to false.

var validate = new Bouncer('form', {
	messageAfterField: false
});

You can also assign a custom location for an error message by including the [data-bouncer-target] attribute on a field. Use a selector for where the message should go as its value.

<label for="email">Your Email Address</label>
<input type="email" name="email" id="email" data-bouncer-target="#email-error">
<p><strong>Why do we need this?</strong> We'll use your email address to send you account information.</p>
<div id="email-error"></div>

API

Bouncer includes smart defaults and works right out of the box.

But if you want to customize things, it also has a robust API that provides multiple ways for you to adjust the default options and settings.

Options and Settings

You can customize validation patterns, error messages, and more by passing and options object into Bouncer during instantiation.

var validate = new Bouncer('form', {

	// Classes & IDs
	fieldClass: 'error', // Applied to fields with errors
	errorClass: 'error-message', // Applied to the error message for invalid fields
	fieldPrefix: 'bouncer-field_', // If a field doesn't have a name or ID, one is generated with this prefix
	errorPrefix: 'bouncer-error_', // Prefix used for error message IDs

	// Patterns
	// Validation patterns for specific input types
	patterns: {
		email: /^([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22))*\x40([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d))*(\.\w{2,})+$/,
		url: /^(?:(?:https?|HTTPS?|ftp|FTP):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-zA-Z\u00a1-\uffff0-9]-*)*[a-zA-Z\u00a1-\uffff0-9]+)(?:\.(?:[a-zA-Z\u00a1-\uffff0-9]-*)*[a-zA-Z\u00a1-\uffff0-9]+)*(?:\.(?:[a-zA-Z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/,
		number: /[-+]?[0-9]*[.,]?[0-9]+/,
		color: /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/,
		date: /(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))/,
		time: /(0[0-9]|1[0-9]|2[0-3])(:[0-5][0-9])/,
		month: /(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2]))/
	},

	// Message Settings
	messageAfterField: true, // If true, displays error message below field. If false, displays it above.
	messageCustom: 'data-bouncer-message', // The data attribute to use for customer error messages
	messageTarget: 'data-bouncer-target', // The data attribute to pass in a custom selector for the field error location

	// Error messages by error type
	messages: {
		missingValue: {
			checkbox: 'This field is required.',
			radio: 'Please select a value.',
			select: 'Please select a value.',
			'select-multiple': 'Please select at least one value.',
			default: 'Please fill out this field.'
		},
		patternMismatch: {
			email: 'Please enter a valid email address.',
			url: 'Please enter a URL.',
			number: 'Please enter a number',
			color: 'Please match the following format: #rrggbb',
			date: 'Please use the YYYY-MM-DD format',
			time: 'Please use the 24-hour time format. Ex. 23:00',
			month: 'Please use the YYYY-MM format',
			default: 'Please match the requested format.'
		},
		outOfRange: {
			over: 'Please select a value that is no more than {max}.',
			under: 'Please select a value that is no less than {min}.'
		},
		wrongLength: {
			over: 'Please shorten this text to no more than {maxLength} characters. You are currently using {length} characters.',
			under: 'Please lengthen this text to {minLength} characters or more. You are currently using {length} characters.'
		}
	},

	// Form Submission
	disableSubmit: false, // If true, native form submission is suppressed even when form validates

	// Custom Events
	emitEvents: true // If true, emits custom events

});

Custom Events

Bouncer emits five custom events:

  • bouncerShowError is emitted on a field when an error is displayed for it.
  • bouncerRemoveError is emitted on a field when an error is removed from it.
  • bouncerFormValid is emitted on a form is successfully validated.
  • bouncerFormInvalid is emitted on a form that fails validation.
  • bouncerInitialized is emitted when bouncer initializes.
  • bouncerDestroy is emitted when bouncer is destroyed.

You can listen for these events with addEventListener. All five events bubble up the DOM. The event.target is the field or form (or document, when initializing and destroying).

// Detect show error events
document.addEventListener('bouncerShowError', function (event) {

	// The field with the error
	var field = event.target;

}, false);

// Detect a successful form validation
document.addEventListener('bouncerFormValid', function (event) {

	// The successfully validated form
	var form = event.target;

	// If `disableSubmit` is true, you might use this to submit the form with Ajax

}, false);

The event.detail object holds event-specific information:

  • On the bouncerShowError event, it has the specific errors for the field.
  • On the bouncerInitialized and bouncerDestroyed events , it contains the settings for the instantiation.
  • On the bouncerFormInvalid event, it includes all of the fields with errors under event.detail.
// Detect show error events
document.addEventListener('bouncerShowError', function (event) {

	// The field with the error
	var field = event.target;

	// The error details
	var errors = event.details.errors;

}, false);

Using Bouncer methods in your own scripts

Bouncer exposes a few public methods that you can use in your own scripts.

validate()

Validate a field. Pass in the field as an argument. Returns an object with validity data.

// Get a field
var field = document.querySelector('#email');

// Validate the field
var bouncer = new Bouncer();
var isValid = bouncer.validate(field);

// Returns an object
isValid = {

	// If true, field is valid
	valid: false,

	// The specific errors
	errors: {
		missingValue: true,
		patternMismatch: true,
		outOfRange: true,
		wrongLength: true
	}

};

// You can also pass in custom options
bouncer.validate(field, {
	// ...
});

validateAll()

Validate all fields in a form or fieldset. Pass in the section as an argument. Returns an array of fields with errors.

// Get a fieldset
var fieldset = document.querySelector('#fieldset');

// Validate the field
var bouncer = new Bouncer();
var areValid = bouncer.validateAll(fieldset);

destroy()

Destroys an instantiated Bouncer instance. Removes any errors from the form and turns validation back over to the browser-native APIs.

// An bouncer instance
var bouncer = new Bouncer('form');

// Destroy it
bouncer.destroy();

Browser Compatibility

Bouncer works in all modern browsers, and IE 9 and above.

Bouncer is built with modern JavaScript APIs, and uses progressive enhancement. If the JavaScript file fails to load, browser-native form validation runs instead

Polyfills

Support back to IE9 requires polyfills for closest(), matches(), classList, and CustomEvent(). Without them, support starts with Edge.

Use the included polyfills version of Bouncer, or include your own.

License

The code is available under the MIT License.

More Repositories

1

smooth-scroll

A lightweight script to animate scrolling to anchor links.
JavaScript
5,481
star
2

reef

A lightweight library for creating reactive, state-based components and UI.
JavaScript
1,113
star
3

gulp-boilerplate

A boilerplate for building web projects with Gulp.js.
JavaScript
846
star
4

kraken

A lightweight, mobile-first boilerplate for front-end web developers.
HTML
768
star
5

gumshoe

A simple vanilla JS scrollspy script.
JavaScript
731
star
6

social-sharing

Add social sharing links and buttons without the bloat.
CSS
641
star
7

tabby

Lightweight, accessible vanilla JS toggle tabs.
JavaScript
577
star
8

atomic

A tiny, Promise-based vanilla JS Ajax/HTTP plugin with great browser support.
HTML
540
star
9

build-tool-boilerplate

A simple boilerplate for using NPM tasks to build and compile JavaScript, CSS, and image files.
JavaScript
488
star
10

validate

A lightweight form validation script.
JavaScript
230
star
11

houdini

A simple, accessible show-and-hide/accordion script.
JavaScript
158
star
12

ebook-boilerplate

A lightweight boilerplate for self-publishing ebooks with markdown and command line.
CSS
135
star
13

htaccess

[DEPRECATED] Best practice server rules for making web pages fast and secure.
ApacheConf
93
star
14

slider

[DEPRECATED] A simple, fluid, touch-enabled carousel.
JavaScript
87
star
15

right-height

Dynamically set content areas of different lengths to the same height.
CSS
87
star
16

modals

Simple modal dialogue windows
JavaScript
85
star
17

bin

A tiny (<1kb) localStorage and sessionStorage helper library.
JavaScript
84
star
18

vanilla-javascript-cheat-sheet

Moved
80
star
19

vanilla-js-toolkit

A growing collection of vanilla JavaScript code snippets, helper functions, polyfills, plugins, and learning resources.
CSS
79
star
20

drop

A small CSS component that turns browser-native <details> elements into dropdown menus.
JavaScript
75
star
21

form-saver

A simple script that lets users save and reuse form data.
JavaScript
68
star
22

hugo-starter

A barebones starter project and theme for learning Hugo.
HTML
67
star
23

gmt-wordpress-for-web-apps

[DEPRECATED] A plugin that provides the essential components you need to power your web app with WordPress.
PHP
67
star
24

events

A tiny event delegation library.
JavaScript
62
star
25

gmt-html-minify

Minify your HTML output in WordPress.
PHP
61
star
26

buoy

[DEPRECATED] A lightweight collection of helper methods for getting stuff done with native JavaScript.
JavaScript
54
star
27

astro

Mobile-first navigation patterns.
JavaScript
53
star
28

sticky-footer

Dynamic, responsive sticky footers.
JavaScript
47
star
29

keel

A lightweight boilerplate for WordPress theme developers.
JavaScript
46
star
30

x-ray

X-Ray is a script that lets users toggle password visibility in forms.
JavaScript
46
star
31

table-of-contents

Automatically generate a table of contents from the headings on the page.
JavaScript
39
star
32

jar

A tiny (< 1kb) library that makes working with cookies easier.
JavaScript
38
star
33

saferInnerHTML

Set the HTML of an element while protecting yourself from XSS attacks.
JavaScript
29
star
34

dom-manipulation-source-code

HTML
24
star
35

gmt-image-compress-and-sharpen

[DEPRECATED] Change the default WordPress compression rate for JPGs, convert images to progressive JPGs, and sharpen images.
PHP
19
star
36

learn-vanilla-js

A roadmap for learning vanilla JavaScript
HTML
19
star
37

jellyfish

[DEPRECATED] A progressively enhanced image lazy loader.
JavaScript
18
star
38

frontend-horse-js-library

The boilerplate for the Frontend Horse livestream.
JavaScript
17
star
39

state-based-ui-source-code

JavaScript
15
star
40

petfinderAPI4everybody

A JavaScript plugin that makes it easier to use the Petfinder API.
JavaScript
14
star
41

vanilla-js-guides

HTML
14
star
42

wp-shopping-cart

[DEPRECATED - DO NOT USE] A simple PayPal shopping cart for WordPress.
PHP
14
star
43

alerts

Simple alert messages.
JavaScript
14
star
44

wp-theme-options

[DEPRECATED] Create a theme options menu that admin can use to adjust theme settings from the dashboard.
PHP
14
star
45

reef-js

The website for ReefJS.
HTML
14
star
46

progress-bars

Simple CSS progress bars.
CSS
13
star
47

writing-libraries-source-code

HTML
13
star
48

learn-vanilla-js-source-code

HTML
12
star
49

kelp

A collection of small functions for creating reactive, state-based UIs.
JavaScript
12
star
50

project-star-rating-system

A vanilla JavaScript project
HTML
11
star
51

gmt-mailchimp-wp-rest-api

Add WP Rest API hooks for JS use of the Mailchimp API.
PHP
11
star
52

apis-source-code

HTML
11
star
53

vanilla-js-list

A growing list of organizations that build sites and apps with vanilla JS
CSS
10
star
54

petfinder-api-for-wordpress

A collection of functions to help you display Petfinder listings on your WordPress site.
PHP
10
star
55

browser-storage-source-code

HTML
10
star
56

gmt-pricing-parity

Display country-specific EDD discounts to visitors.
PHP
9
star
57

vanilla-js-academy

PHP
9
star
58

gmt-remove-header-junk

[DEPRECATED] Remove the unneccessary junk WordPress adds to the header.
PHP
9
star
59

snapshot

Simple image styling.
CSS
9
star
60

dom-injection-source-code

HTML
8
star
61

the-lean-web

CSS
8
star
62

sw-fonts

Demo for https://gomakethings.com/improving-web-font-performance-with-service-workers/
HTML
8
star
63

gmt-paypal-ipn-forwarder

[DEPRECATED] Forward PayPal IPN to multiple other IPN services in WordPress.
PHP
8
star
64

service-worker-pages-demo

A demo for https://gomakethings.com/saving-recently-viewed-pages-with-service-workers-and-vanilla-js/
HTML
8
star
65

gmt-slack-invites-for-edd

[DEPRECATED] Automatically invite members to your Slack team when they purchase a product.
PHP
8
star
66

vanilla-js-guidebook-labs

The labs for "The Vanilla JS Guidebook."
HTML
7
star
67

service-workers-source-code

JavaScript
7
star
68

sw-offline-first

Demo for https://gomakethings.com/offline-first-with-service-workers-and-vanilla-js/
HTML
7
star
69

harbor-wp-theme

A free mobile-friendly WordPress theme built specifically for animal rescue organizations.
JavaScript
7
star
70

adventure

A simplish, imagination-based kitchen table RPG.
CSS
7
star
71

vanilla-js-projects

HTML
6
star
72

games

Simple vanilla JS game demos.
HTML
6
star
73

variable-function-scope-source-code

HTML
6
star
74

gmt-automated-slack-invites

Automate Slack invites with WordPress.
PHP
6
star
75

google-hosted-jquery

[DEPRECATED] Use the Google CDN version of jQuery in WordPress, with a local fallback.
PHP
6
star
76

gomakethings

The WordPress theme for Go Make Things
JavaScript
5
star
77

service-worker-demo

A demo for https://gomakethings.com/writing-your-first-service-worker-with-vanilla-js/
HTML
5
star
78

origami

[DEPRECATED] Origami has been merged into the Kraken front-end boilerplate and is no longer supported.
HTML
5
star
79

timezones

Easily calculate timezones for your next meeting.
HTML
4
star
80

arrays-objects-source-code

HTML
4
star
81

labels

Lightweight CSS labels.
CSS
4
star
82

gmt-antispambot

A shortcode for the antispambot function that's built into WordPress.
PHP
4
star
83

cferdinandi

4
star
84

web-components-source-code

HTML
4
star
85

string-array-object-source-code

HTML
4
star
86

vanilla-js-podcast

A show about JavaScript for people who hate the complexity of modern front‑end web development.
CSS
4
star
87

es-modules-demo

https://gomakethings.com/an-intro-to-import-and-export-with-es-modules/
JavaScript
3
star
88

javascript-essentials-source-code

Source code for the JavaScript Essentials workshop.
HTML
3
star
89

accessible-components-source-code

HTML
3
star
90

es-modules-source-code

JavaScript
3
star
91

ajax-source-code

HTML
3
star
92

strings-numbers-source-code

HTML
3
star
93

go-mobile-first

[DEPRECATED] A mobile-first boilerplate for WordPress.
PHP
3
star
94

gmt-allow-iframes

[DEPRECATED] Prevent wp_kses from removing iframe embeds
PHP
3
star
95

gmt-donations

[DEPRECATED] A WordPress plugin that lets you create powerful donation forms that integrate with Stripe and PayPal Express Checkout.
PHP
3
star
96

token-based-authentication-source-code

HTML
3
star
97

advanced-academy-common-issue-not-modularizing-your-code-enough

JavaScript
3
star
98

fetch

The Fetch for Petfinder plugin
JavaScript
2
star
99

es-modules-default

https://gomakethings.com/how-to-define-a-default-export-with-vanilla-js-es-modules/
JavaScript
2
star
100

careers

CSS
2
star