reveal

Cover

‧ 極意?!

Paul Li

Profile

Paul Li

  • - front-end engineer
  • - HTML / JavaScript / CSS
  • - work @ Yahoo
  • - GDE on web category
  • - facebook: mei.studio.li
  • #DoRightThings

Agenda

Agenda

  • - Pick suitable elements
  • - Style your form
  • - Amazing supports
  • - Wonderful partners
  • - WC. the convenience

Pick suitable elements

Pick elements

The input element

The input element

There are som many kinds <input /> for different purpose. Such as email、url、color、file、datetime、search、text、number、range...etc. This almost cover all kinds scenarios. Web developers could apply these input elements to build a vivid form UI/UX.

Pick suitable elements

Use case 1.

Take <input /> UI / UX and methods to indicate various adjustments.

Pick suitable elements

Use case 2.

With input[type=range] supported, Developers could save 「drag」、「drop」 event usage and don't need to detect current device.

Style your form

Style your form

CSS > accent-color

unfold

CSS > accent-color

The accent-color CSS property sets the accent color for user-interface controls generated by some elements.

<style> :nth-child(2 of .row) input { accent-color: #eb0f29; } :nth-child(3 of .row) input { accent-color: rgba(26 197 103); } </style>

Customize input element

unfold

Customize input element

With modern CSS supports, web developers could style <input /> easily ever.

<div class="input-set"> <input name="input-name" type="text" placeholder="placeholder" /> <label class="input-set__label"> <span class="input-set__label__span">label</span> </label> <em class="input-set__em"></em> </div> <style> .input-set { --background-color: var(--default-background-color, rgba(var(--white))); --text-color: var(--default-text-color, rgba(var(--batcave))); --border-color: var(--default-border-color, rgba(var(--dolphin))); --err-color: var(--default-err-color, rgba(var(--solo-cup))); --err-text-color: var(--default-err-text-color, rgba(var(--dolphin))); --label-text-color: var(--default-label-text-color, rgba(var(--dolphin))); --theme: var(--default-theme, rgba(var(--dory))); --hover: var(--theme); /* readyonly, disabled */ --background-color-disabled: var(--default-background-color-disabled, rgba(var(--grey-hair))); --text-color-disabled: var(--default-text-color-disabled, rgba(var(--bob))); --border-color-disabled: var(--default-border-color-disabled, rgba(var(--bob))); --placeholder-text-color-normal: transparent; --placeholder-text-color-active: var(--default-placeholder-text-color, rgba(var(--charcoal))); --placeholder-text-color: var(--placeholder-text-color-normal); ... ... } .input-set > input::-webkit-input-placeholder { color: var(--placeholder-text-color); transition: color 100ms ease; will-change: color; } .input-set > input::-moz-placeholder { color: var(--placeholder-text-color); transition: color 100ms ease; will-change: color; } .input-set { position: relative; display: block; } .input-set > input { appearance: none; ... ... } .input-set > input:focus { outline: 0 none; } .input-set__label { ... } .input-set__label__span { ... } .input-set__label::after { ... } .input-set__em { ... } .input-set__em::before { ... } /* focus */ .input-set > input:focus { --border-color: var(--hover); --border-size: var(--border-size-active); } .input-set > input:focus::-webkit-input-placeholder { --placeholder-text-color: var(--placeholder-text-color-active); } .input-set > input:focus::-moz-placeholder { --placeholder-text-color: var(--placeholder-text-color-active); } .input-set > input:focus ~ .input-set__label { color: var(--hover); transform: translateY(-24px) scale(0.85); } .input-set > input:not(:placeholder-shown) ~ .input-set__label { transform: translateY(-24px) scale(0.85); } /* invalid */ .input-set > input:invalid ~ .input-set__em, .input-set > input[invalid] ~ .input-set__em { --err-display: flex; } .input-set > input:invalid, .input-set > input[invalid] { --border-color: var(--err-color); } /* inert, readonly, disabled */ [inert] .input-set, .input-set:has(input[readonly]), .input-set:has(input[disabled]) { --default-text-color: var(--text-color-disabled); --default-border-color: var(--border-color-disabled); --default-background-color: var(--background-color-disabled); } @media (hover: hover) { .input-set:hover { --border-color: var(--hover); } .input-set:hover > input:not(:placeholder-shown) ~ .input-set__label { --label-text-color: var(--hover); } } @media (prefers-color-scheme: dark) { .input-set { ... ... } } </style>

Customize select element

unfold

Customize select element

With modern CSS supports, web developers could style <select /> easily ever.

<div class="input-set input-set--select"> <select name="select-name"> <optgroup label="Group A"> <option value="1">option 1</option> <option value="2">option 2</option> <option value="3">option 3</option> </optgroup> <optgroup label="Group B"> <option value="4">option 4</option> <option value="5">option 5</option> <option value="6">option 6</option> </optgroup> </select> <labelclass="input-set__label"> <span class="input-set__label__span">label</span> </label> <em class="input-set__em"></em> </div> <style> .input-set { --background-color: var(--default-background-color, rgba(var(--white))); --text-color: var(--default-text-color, rgba(var(--batcave))); --border-color: var(--default-border-color, rgba(var(--dolphin))); --err-color: var(--default-err-color, rgba(var(--solo-cup))); --err-text-color: var(--default-err-text-color, rgba(var(--dolphin))); --label-text-color: var(--default-label-text-color, rgba(var(--dolphin))); --theme: var(--default-theme, rgba(var(--dory))); --hover: var(--theme); /* readyonly, disabled */ --background-color-disabled: var(--default-background-color-disabled, rgba(var(--grey-hair))); --text-color-disabled: var(--default-text-color-disabled, rgba(var(--bob))); --border-color-disabled: var(--default-border-color-disabled, rgba(var(--bob))); --placeholder-text-color-normal: transparent; --placeholder-text-color-active: var(--default-placeholder-text-color, rgba(var(--charcoal))); --placeholder-text-color: var(--placeholder-text-color-normal); ... ... } /* select */ .input-set--select select { padding-inline-end: calc(var(--padding-inline-end) * 2 + 24px); } .input-set--select::after { ... } .input-set > select::-webkit-input-placeholder { color: var(--placeholder-text-color); transition: color 100ms ease; will-change: color; } .input-set > select::-moz-placeholder { color: var(--placeholder-text-color); transition: color 100ms ease; will-change: color; } .input-set { position: relative; display: block; } .input-set > select { appearance: none; ... ... } .input-set > select:focus { outline: 0 none; } .input-set__label { ... } .input-set__label__span { ... } .input-set__label::after { ... } .input-set__em { ... } .input-set__em::before { ... } /* focus */ .input-set > select:focus { --border-color: var(--hover); --border-size: var(--border-size-active); } .input-set > select:focus::-webkit-input-placeholder { --placeholder-text-color: var(--placeholder-text-color-active); } .input-set > select:focus::-moz-placeholder { --placeholder-text-color: var(--placeholder-text-color-active); } .input-set > select:focus ~ .input-set__label { color: var(--hover); transform: translateY(-24px) scale(0.85); } .input-set > select:not(:placeholder-shown) ~ .input-set__label { transform: translateY(-24px) scale(0.85); } /* invalid */ .input-set > select:invalid ~ .input-set__em, .input-set > select[invalid] ~ .input-set__em { --err-display: flex; } .input-set > select:invalid, .input-set > select[invalid] { --border-color: var(--err-color); } /* inert, readonly, disabled */ [inert] .input-set, .input-set:has(select[readonly]), .input-set:has(select[disabled]) { --default-text-color: var(--text-color-disabled); --default-border-color: var(--border-color-disabled); --default-background-color: var(--background-color-disabled); } @media (hover: hover) { .input-set:hover { --border-color: var(--hover); } .input-set:hover > select:not(:placeholder-shown) ~ .input-set__label { --label-text-color: var(--hover); } } @media (prefers-color-scheme: dark) { .input-set { ... ... } } </style>

Customize checkbox element

unfold

Customize checkbox element

With modern CSS supports, web developers could style input[type=checkbox] easily ever.

<div class="checkbox-set"> <input id="my-checkbox" type="checkbox" name="checkbox[]" value="0" /> <label class="checkbox-set__label" for="my-checkbox"></label> </div> <style> .checkbox-set { /* normal */ --accent-color: var(--default-accent-color, rgba(42 108 246)); --border-color: var(--default-border-color, rgba(131 138 146)); --background-color: var(--default-background-color, rgba(255 255 255)); --checkmark-color: var(--default-checkmark-color, rgba(255 255 255)); /* disabled */ --border-color--disabled: var(--default-border-color--disabled, rgba(178 185 192)); --background-color--disabled: var(--default-background-color--disabled, rgba(255 255 255)); --checkmark-color--disabled: var(--default-checkmark-color--disabled, rgba(178 185 192)); ... ... } .checkbox-set__label { --checkmark-opacity-normal: 0; --checkmark-opacity-active: 1; --checkmark-opacity: var(--checkmark-opacity-normal); } .checkbox-set input:checked ~ .checkbox-set__label { --border-color: var(--accent-color); --background-color: var(--accent-color); --checkmark-opacity: var(--checkmark-opacity-active); } .checkbox-set input:not(:disabled):focus-visible ~ .checkbox-set__label { --border-color: var(--accent-color); } [inert] .checkbox-set input ~ .checkbox-set__label, .checkbox-set input:disabled ~ .checkbox-set__label { --border-color: var(--border-color--disabled); --background-color: var(--background-color--disabled); --checkmark-color: var(--checkmark-color--disabled); } .checkbox-set { position: relative; inline-size: var(--size); aspect-ratio: 1/1; overflow: hidden; } .checkbox-set input { position: absolute; top: -100%; outline: 0 none; } .checkbox-set__label { ... } .checkbox-set__label::before { ... } .checkbox-set__label::after { ... } @media (hover: hover) { .checkbox-set input:not(:disabled) ~ .checkbox-set__label:hover { --border-color: var(--accent-color); } } @media (prefers-color-scheme: dark) { .checkbox-set { ... ... } } </style>

Customize radio buttons element

unfold

Customize radio buttons element

With modern CSS supports, web developers could style input[type=radio] easily ever.

<div class="radio-set"> <input id="my-radio" type="radio" name="radio" value="0" /> <label class="radio-set__label" for="my-radio"></label> </div> <style> .radio-set { /* normal */ --accent-color: var(--default-accent-color, rgba(42 108 246)); --border-color: var(--default-border-color, rgba(131 138 146)); --background-color: var(--default-background-color, rgba(255 255 255)); --default-dot-color: var(--default-dot-color, rgba(accent-color)); /* disabled */ --border-color--disabled: var(--default-border-color--disabled, rgba(178 185 192)); --background-color--disabled: var(--default-background-color--disabled, rgba(255 255 255)); --default-dot-color--disabled: var(--default-accent-color, rgba(178 185 192)); ... ... } .radio-set__label { --dot-scale-normal: .001; --dot-scale-active: 1; --dot-scale: var(--dot-scale-normal); } .radio-set input:checked ~ .radio-set__label { --border-color: var(--accent-color); --dot-scale: var(--dot-scale-active); } .radio-set input:not(:disabled):focus-visible ~ .radio-set__label { --border-color: var(--accent-color); } .radio-set input:disabled ~ .radio-set__label { --border-color: var(--border-color--disabled); --background-color: var(--background-color--disabled); --dot-color: var(--dot-color--disabled); } .radio-set { position: relative; inline-size: var(--border-radius); aspect-ratio: 1/1; overflow: hidden; } .radio-set input { position: absolute; top: -100%; outline: 0 none; } .radio-set__label { ... } .radio-set__label::before { ... } .radio-set__label::after { ... } @media (hover: hover) { .radio-set input:not(:disabled) ~ .radio-set__label:hover { --border-color: var(--accent-color); } } @media (prefers-color-scheme: dark) { .radio-set { ... ... } } </style>

Avoid misunderstanding

Avoid misunderstanding

Sometimes you might face some embarrassed situations. Such as 「radiobox」or「checkbutton」。

Amazing Supports

Amazing Supports

Input & Web API

unfold

Input & Web API

Some <input /> exposed some interesting web api for developers for vivid usage.

<script> // input[type=color] const inputColor = document.querySelector('input[type=color]'); inputColor.showPicker(); // EyeDropper document.querySelector('.button--eyedropper') .addEventListener('click', async () => { const eyeDropper = new EyeDropper(); const result = await eyeDropper.open(); const colorHexValue = result.sRGBHex; } ); // input[type=datetime-local] const inputDatetime = document.querySelector('input[type="datetime-local"]'); inputDatetime.showPicker(); // input[type=number] const inputNumber = document.querySelector('input[type=number]'); document.querySelector('.button--plus') .addEventListener('click', () => { inputNumber.stepUp(); } ); document.querySelector('.button--minus') .addEventListener('click', () => { inputNumber.stepDown(); } ); // input and select() const inputSelectContent = document.querySelector('.input--selectContent'); inputSelectContent.select(); </script>

Validation

unfold

Validation

form element has many interesting attributes. Web developers could apply them for validation purpose. We never know it's such a piece of cake for validation, ever.

<form action="..."> ... ... <input type="..." value="..." accept=".jpg,.jpeg,.png,.gif,.webp" min="1" max="3000000" minlength="1" maxlength="10" step="5" pattern="https://((youtu.be)|(youtube.com)|(www.youtube.com))/.*" multiple required readonly disabled /> ... ... </form> <script type="module"> const controller = new AbortController(); const { signal } = controller; const form = document.querySelector('form'); const transformErrorMessage = (inputEle) => { let message = ''; const validity = inputEle.validity; const { validityDefault, validityRangeOverflow, validityRangeUnderflow, validityValueMissing } = inputEle.dataset; switch (true) { case validity.valueMissing: message = validityValueMissing || '此為必填欄位'; break; case validity.rangeOverflow: message = validityRangeOverflow || '內容已超過最大值'; break; case validity.rangeUnderflow: message = validityRangeUnderflow || '內容低於最小值'; break; default: // patternMismatch, badInput, customError, stepMismatch, tooLong, tooShort, typeMismatch message = validityDefault || '格式錯誤,請再確認'; break; } return message; } const onInput = (evt) => { const input = evt.target.closest('input'); if (!input) { return; } input.toggleAttribute('invalid', false); }; const onInvalid = (evt) => { evt.preventDefault(); const inputSet = evt.target.closest('.input-set'); const input = evt.target.closest('input'); if (!inputSet) { return; } const errElement = inputSet.querySelector('.input-set__em'); errElement.textContent = transformErrorMessage(evt.target); input.toggleAttribute('invalid', true); }; const onSubmit = (evt) => { evt.preventDefault(); if (form.hasAttribute('inert')) { return; } form.toggleAttribute('inert', true); // native error checks const flag = form.checkValidity(); if (!flag) { // errors occured ... form.toggleAttribute('inert', false); return; } const formData = new FormData(form); const dataObject = Object.fromEntries(formData.entries()); ... ... ... form.toggleAttribute('inert', false); }; // events form.addEventListener('invalid', onInvalid, { signal, capture: true }); form.addEventListener('input', onInput, { signal }); form.addEventListener('submit', onSubmit, { signal }); </script>

Usability

unfold

Usability

After validation, web developers should think how to enhance usability. There are more and more web api support for this. Sucs as autocapitalize、autocorrect、autocomplete、inputmode and enterkeyhint. Let's take a quick view.

<form action="..."> ... ... <input type="..." value="..." autocapitalize="..." autocorrect="..." autocomplete="..." enterkeyhint="go" inputmode="numeric" /> ... ... </form>
10:21

tw.bid.yahoo.com

Wonderful partners

Wonderful partners

CSS > Logical Properties

unfold

CSS > Logical Properties

With modern CSS supports, web developers could style <input /> easily ever.

<div class="input-set"> <input name="input-name" type="text" placeholder="placeholder" /> <label class="input-set__label"> <span class="input-set__label__span">label</span> </label> <em class="input-set__em"></em> </div> <style> .input-set { --background-color: var(--default-background-color, rgba(var(--white))); --text-color: var(--default-text-color, rgba(var(--batcave))); --border-color: var(--default-border-color, rgba(var(--dolphin))); --err-color: var(--default-err-color, rgba(var(--solo-cup))); --err-text-color: var(--default-err-text-color, rgba(var(--dolphin))); --label-text-color: var(--default-label-text-color, rgba(var(--dolphin))); --theme: var(--default-theme, rgba(var(--dory))); --hover: var(--theme); /* readyonly, disabled */ --background-color-disabled: var(--default-background-color-disabled, rgba(var(--grey-hair))); --text-color-disabled: var(--default-text-color-disabled, rgba(var(--bob))); --border-color-disabled: var(--default-border-color-disabled, rgba(var(--bob))); --placeholder-text-color-normal: transparent; --placeholder-text-color-active: var(--default-placeholder-text-color, rgba(var(--charcoal))); --placeholder-text-color: var(--placeholder-text-color-normal); ... ... } .input-set > input::-webkit-input-placeholder { color: var(--placeholder-text-color); transition: color 100ms ease; will-change: color; } .input-set > input::-moz-placeholder { color: var(--placeholder-text-color); transition: color 100ms ease; will-change: color; } .input-set { position: relative; display: block; } .input-set > input { appearance: none; ... ... } .input-set > input:focus { outline: 0 none; } .input-set__label { ... } .input-set__label__span { ... } .input-set__label::after { ... } .input-set__em { ... } .input-set__em::before { ... } /* focus */ .input-set > input:focus { --border-color: var(--hover); --border-size: var(--border-size-active); } .input-set > input:focus::-webkit-input-placeholder { --placeholder-text-color: var(--placeholder-text-color-active); } .input-set > input:focus::-moz-placeholder { --placeholder-text-color: var(--placeholder-text-color-active); } .input-set > input:focus ~ .input-set__label { color: var(--hover); transform: translateY(-24px) scale(0.85); } .input-set > input:not(:placeholder-shown) ~ .input-set__label { transform: translateY(-24px) scale(0.85); } /* invalid */ .input-set > input:invalid ~ .input-set__em, .input-set > input[invalid] ~ .input-set__em { --err-display: flex; } .input-set > input:invalid, .input-set > input[invalid] { --border-color: var(--err-color); } /* inert, readonly, disabled */ [inert] .input-set, .input-set:has(input[readonly]), .input-set:has(input[disabled]) { --default-text-color: var(--text-color-disabled); --default-border-color: var(--border-color-disabled); --default-background-color: var(--background-color-disabled); } @media (hover: hover) { .input-set:hover { --border-color: var(--hover); } .input-set:hover > input:not(:placeholder-shown) ~ .input-set__label { --label-text-color: var(--hover); } } @media (prefers-color-scheme: dark) { .input-set { ... ... } } </style>

CSS > :has()

unfold

CSS > :has()

:has() is so powerful for condition syntax, it could replace some JavaScript supports.

<div class="wrap"> <div class="unit"> <div> <input id="radioA" type="radio" name="my-radio" value="0" /> </div> <p class="hidden-content">hidden information</p> </div> <div class="unit"> <div> <input id="radioB" type="radio" name="my-radio" value="1" /> </div> <p class="hidden-content">hidden information</p> </div> </div> <style> .hidden-content { display: none; } .wrap:has(#radioA:checked) :nth-child(1 of .unit) .hidden-content { display: block; } .wrap:has(#radioB:checked) :nth-child(2 of .unit) .hidden-content { display: block; } </style>

input ?! select ?!

unfold

input ?! select ?!

<input /> could associate <datalist /> through attribute list to let input has dropdown menu just like <select />.

<input name="my-input" type="text" placeholder="placeholder" autocomplete="off" list="my-datalist" /> <datalist id="my-datalist"> <option value="option 1">option 1</option> <option value="option 2">option 2</option> <option value="option 3">option 3</option> <option value="option 4">option 4</option> <option value="option 5">option 5</option> </datalist>

details

unfold

details

<details /> provide toggle feature to disaply or hide information. It's suitable for tip or extra information.

<details class="tip"> <summary class="tip__summary"> summary </summary> <p class="tip__content"> content </p> </details>

dialog

unfold

dialog

The <dialog /> HTML element represents a dialog box or other interactive component, such as a dismissible alert, inspector, or subwindow.

form & inert

unfold

form & inert

It's time to apply 「inert」to freeze or unfreeze <form />.

<style> [inert] :is(input, select) { ... ... } </style> <script> const form = document.querySelector('form'); form.addEventListener('submit', (evt) => { ... ... form.inert = true; // freeze || unfreeze form ... ... } ); </script>

Web Components the convenience

WC. the convenience

Input Assistant

Input Assistant

<msc-input-assistant /> is a web component which help user to input wisely. Users could search or pick option with list we provide. Once options are not good enough for them, they can 「add」 their own options for usage.

Image Uploader

Image Uploader

<msc-image-uploader /> is a web component based image uploader. Users could pick & upload images by 「file picker」、「drag & drop」and even direct「paste」image content. Besides that, users could also change images sorting through 「drag & drop」or 「keyboard arrow keys」.

Tags Collector

Tags Collector

<msc-tags-collector /> is a web component for input tags purpose. Users could key in anything they like (include space). Besides that sorting has already build-in with <msc-tags-collector />. We can go by 「DRAG」and「DROP」or ←、→ to arrange tag orders we like.

Help me write

Help me write

「OpenAI」 is so powerful for all kinds services. That's why I try to adopt OpenAI to help Y! Auction's seller submit merchandise quickly.

Recap

Recap

  • - Pick suitable elements
  • - Style your form
  • - Use element & Web API wisely
  • - Amazing Web Components

thankful

Thanks, all of you.