Stay organized with collections
Save and categorize content based on your preferences.
HTML
<main>
<h1>Settings</h1>
<form>
<section>
<header>
<h2>Sound & vibration</h2>
<small>Adjust system volume channels</small>
</header>
<fieldset>
<div class="fieldset-item">
<picture aria-hidden="true">
<svg viewBox="0 0 24 24">
<title>A note icon</title>
<path d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"/>
</svg>
</picture>
<div class="input-stack">
<label
for="media-volume"
id="media-volume"
aria-hidden="true">
Media volume
</label>
<input
name="media-volume"
aria-labelledby="media-volume"
type="range"
value="3"
max="10"
style="--track-fill: 30%"
>
</div>
</div>
<div class="fieldset-item">
<picture aria-hidden="true">
<title>A phone icon</title>
<svg viewBox="0 0 24 24">
<path d="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z"/>
</svg>
</picture>
<div class="input-stack">
<label for="call-volume" id="call-volume" aria-hidden="true">Call volume</label>
<input
name="call-volume"
aria-labelledby="call-volume"
type="range"
value="7"
max="10"
style="--track-fill: 70%"
>
</div>
</div>
<div class="fieldset-item">
<picture aria-hidden="true">
<svg viewBox="0 0 24 24">
<title>A bell icon</title>
<path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"/>
</svg>
</picture>
<div class="input-stack">
<label for="ring-volume" id="ring-volume" aria-hidden="true">Ring volume</label>
<input
name="ring-volume"
aria-labelledby="ring-volume"
type="range"
value="5"
max="10"
style="--track-fill: 50%"
>
</div>
</div>
<div class="fieldset-item">
<picture aria-hidden="true">
<svg viewBox="0 0 24 24">
<title>An alarm clock icon</title>
<path d="M22 5.72l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM7.88 3.39L6.6 1.86 2 5.71l1.29 1.53 4.59-3.85zM12.5 8H11v6l4.75 2.85.75-1.23-4-2.37V8zM12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9c4.97 0 9-4.03 9-9s-4.03-9-9-9zm0 16c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/>
</svg>
</picture>
<div class="input-stack">
<label for="alarm-volume" id="alarm-volume" aria-hidden="true">Alarm volume</label>
<input
name="alarm-volume"
aria-labelledby="alarm-volume"
type="range"
value="8"
max="10"
style="--track-fill: 80%"
>
</div>
</div>
</fieldset>
</section>
<section>
<header>
<h2>Notifications</h2>
<small>Turn specific channels on/off</small>
</header>
<fieldset>
<div class="fieldset-item">
<input
type="checkbox"
checked
id="text-notifications"
name="text-notifications"
>
<div class="input-stack">
<label for="text-notifications">
<h3>Text Messages</h3>
<small>Get notified about all text messages sent to your device</small>
</label>
</div>
</div>
<div class="fieldset-item">
<input
type="checkbox"
id="voice-notifications"
name="voice-notifications"
>
<div class="input-stack">
<label for="voice-notifications">
<h3>Voice Mail</h3>
<small>Get notified about all voice messages sent to your device</small>
</label>
</div>
</div>
<div class="fieldset-item">
<input
type="checkbox"
id="email-notifications"
name="email-notifications"
>
<div class="input-stack">
<label for="email-notifications">
<h3>Emails</h3>
<small>Get notified about all text messages to your device</small>
</label>
</div>
</div>
</fieldset>
</section>
</form>
</main>
CSS
@custom-media --motionOK (prefers-reduced-motion: no-preference);
:root {
--surface1: lch(10 0 0);
--surface2: lch(15 0 0);
--surface3: lch(20 0 0);
--surface4: lch(25 0 0);
--text1: lch(95 0 0);
--text2: lch(75 0 0);
--brand: lch(64 20 237);
--brand-bg1: lch(70 64 349);
--brand-bg2: lch(60 84 300);
--brand-bg-gradient: linear-gradient(
to bottom,
var(--brand-bg1),
var(--brand-bg2)
);
--thumb-highlight-color: lch(100 0 0 / 20%);
--space-xxs: .25rem;
--space-xs: .5rem;
--space-sm: 1rem;
--space-md: 1.5rem;
--space-lg: 2rem;
--space-xl: 3rem;
--space-xxl: 6rem;
--isLTR: 1;
--isRTL: -1;
&:dir(rtl) {
--isLTR: -1;
--isRTL: 1;
}
@media (prefers-color-scheme: light) {
& {
--surface1: lch(90 0 0);
--surface2: lch(100 0 0);
--surface3: lch(98 0 0);
--surface4: lch(85 0 0);
--text1: lch(20 0 0);
--text2: lch(40 0 0);
--brand: lch(64 40 237);
--brand-bg1: lch(50 64 349);
--brand-bg2: lch(40 84 300);
--thumb-highlight-color: lch(0 0 0 / 20%);
}
}
}
html {
block-size: 100%;
inline-size: 100%;
}
body {
min-block-size: 100%;
min-inline-size: 100%;
box-sizing: border-box;
margin: 0;
padding-block: var(--space-xs);
background: var(--surface1);
color: var(--text1);
font-family: system-ui, sans-serif;
}
h1,h2,h3 {
margin: 0;
font-weight: 500;
}
main {
display: grid;
gap: var(--space-xl);
place-content: center;
padding: var(--space-sm);
@media (width >= 540px) {
padding: var(--space-lg);
}
@media (width >= 800px) {
padding: var(--space-xl);
}
}
form {
max-width: 89vw;
display: grid;
gap: var(--space-xl) var(--space-xxl);
--repeat: auto-fit;
grid-template-columns:
repeat(var(--repeat), minmax(min(10ch, 100%), 35ch));
align-items: flex-start;
@media (orientation: landscape) and (width >= 640px) {
--repeat: 2;
}
}
section {
display: grid;
gap: var(--space-md);
}
header {
display: grid;
gap: var(--space-xxs);
}
fieldset {
border: 1px solid var(--surface4);
background: var(--surface4);
padding: 0;
margin: 0;
display: grid;
gap: 1px;
border-radius: var(--space-sm);
overflow: hidden;
transition: box-shadow .3s ease;
&:focus-within {
box-shadow: 0 5px 20px -10px hsl(0 0% 0% / 50%);
}
}
input[type="range"] {
--track-height: .5ex;
--track-fill: 0%;
--thumb-size: 3ex;
--thumb-offset: -1.25ex;
--thumb-highlight-size: 0px;
display: block;
inline-size: 100%;
margin: 1ex 0;
appearance: none;
background: transparent;
outline-offset: 5px;
@media (hover: none) {
--thumb-size: 30px;
--thumb-offset: -14px;
}
&::-webkit-slider-runnable-track {
appearance: none;
block-size: var(--track-height);
border-radius: 5ex;
background:
linear-gradient(
to right,
transparent var(--track-fill),
var(--surface1) 0%
),
var(--brand-bg-gradient) fixed;
}
&::-moz-range-track {
appearance: none;
block-size: var(--track-height);
border-radius: 5ex;
background:
linear-gradient(
to right,
transparent var(--track-fill),
var(--surface1) 0%
),
var(--brand-bg-gradient) fixed;
}
&::-webkit-slider-thumb {
appearance: none;
cursor: ew-resize;
border: 3px solid var(--surface3);
block-size: var(--thumb-size);
inline-size: var(--thumb-size);
margin-block-start: var(--thumb-offset);
border-radius: 50%;
background: var(--brand-bg-gradient) fixed;
box-shadow: 0 0 0 var(--thumb-highlight-size) var(--thumb-highlight-color);
@media (--motionOK) {
transition: box-shadow .1s ease;
}
@nest .fieldset-item:focus-within & {
border-color: var(--surface2);
}
}
&::-moz-range-thumb {
appearance: none;
cursor: ew-resize;
border: 3px solid var(--surface3);
block-size: var(--thumb-size);
inline-size: var(--thumb-size);
margin-block-start: var(--thumb-offset);
border-radius: 50%;
background: var(--brand-bg-gradient) fixed;
box-shadow: 0 0 0 var(--thumb-highlight-size) var(--thumb-highlight-color);
@media (--motionOK) {
transition: box-shadow .1s ease;
}
@nest .fieldset-item:focus-within & {
border-color: var(--surface2);
}
}
&:is(:hover,:active) {
--thumb-highlight-size: 10px;
}
}
input[type="checkbox"] {
inline-size: var(--space-sm);
block-size: var(--space-sm);
margin: 0;
outline-offset: 5px;
accent-color: var(--brand);
position: relative;
transform-style: preserve-3d;
cursor: pointer;
&:hover::before {
--thumb-scale: 1;
}
@media (hover: none) {
inline-size: var(--space-md);
block-size: var(--space-md);
}
&::before {
--thumb-scale: .01;
--thumb-highlight-size: var(--space-xl);
content: "";
inline-size: var(--thumb-highlight-size);
block-size: var(--thumb-highlight-size);
clip-path: circle(50%);
position: absolute;
inset-block-start: 50%;
inset-inline-start: 50%;
background: var(--thumb-highlight-color);
transform-origin: center center;
transform:
translateX(calc(var(--isRTL) * 50%))
translateY(-50%)
translateZ(-1px)
scale(var(--thumb-scale))
;
will-change: transform;
@media (--motionOK) {
transition: transform .2s ease;
}
}
}
.fieldset-item {
background: var(--surface3);
transition: background .2s ease;
display: grid;
grid-template-columns: var(--space-lg) 1fr;
gap: var(--space-md);
padding-block: var(--space-sm);
padding-inline: var(--space-md);
@media (width >= 540px) {
grid-template-columns: var(--space-xxl) 1fr;
gap: var(--space-xs);
padding-block: var(--space-md);
padding-inline: 0 var(--space-xl);
}
&:focus-within {
background: var(--surface2);
& svg {
fill: white;
}
& picture {
clip-path: circle(50%);
background: var(--brand-bg-gradient) fixed;
}
}
& > :is(.input-stack, label) {
display: grid;
gap: var(--space-xs);
}
& > .input-stack > label {
display: contents;
}
& > picture {
block-size: var(--space-xl);
inline-size: var(--space-xl);
clip-path: circle(40%);
display: inline-grid;
place-content: center;
background: var(--surface3) fixed;
@media (--motionOK) {
transition: clip-path .3s ease;
}
}
& svg {
fill: var(--text2);
block-size: var(--space-md);
}
& > :is(picture, input[type="checkbox"]) {
place-self: center;
}
}
small {
color: var(--text2);
line-height: 1.5;
}
JS
const form = document.querySelector('form')
const sliders = document.querySelectorAll('input[type="range"]')
const rangeToPercent = slider => {
const max = slider.getAttribute('max') || 10
const percent = slider.value / max * 100
return `${parseInt(percent)}%`
}
sliders.forEach(slider => {
slider.style.setProperty('--track-fill', rangeToPercent(slider))
slider.addEventListener('input', e => {
e.target.style.setProperty('--track-fill', rangeToPercent(e.target))
})
})
form.addEventListener('input', e => {
const formData = Object.fromEntries(new FormData(form))
console.table(formData)
})
Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2023-07-05 UTC.
[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2023-07-05 UTC."],[],[]]