Shades
React + Tailwind
import { useId } from 'react';
const css = `
.toggle-shades { display: inline-flex; align-items: center; gap: 14px; color: light-dark(#42454c, #e5e6e9); font-size: 15px; cursor: pointer; user-select: none; }
.toggle-shades__input { position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0; }
.toggle-shades__art { display: block; width: 124px; height: 50px; overflow: visible; border-radius: 8px; }
.toggle-shades__input:focus-visible + .toggle-shades__art { outline: 2px solid light-dark(#5f626a, #a7abb2); outline-offset: 4px; }
.toggle-shades__glass { fill: light-dark(#dbdde1, #7d8087); }
.toggle-shades__eye { fill: light-dark(#373a40, #121417); }
.toggle-shades__frame { fill: none; stroke: light-dark(#50535b, #c6c8cd); stroke-width: 2.6; stroke-linejoin: round; stroke-linecap: round; }
/* The tint is dark in both themes. It runs down from the top like a photochromic lens. */
.toggle-shades__top { stop-color: light-dark(#50535b, #393c41); }
.toggle-shades__bottom { stop-color: light-dark(#373a40, #121417); }
.toggle-shades__shine { fill: none; stroke: light-dark(#fbfcfd, #f1f2f3); stroke-width: 1.6; stroke-linecap: round; opacity: 0; transition: opacity 0.3s; }
.toggle-shades__input:checked + .toggle-shades__art .toggle-shades__shine { opacity: 0.35; transition-delay: 0.4s; }
.toggle-shades__tint { transform-box: view-box; transform-origin: 0 12px; transform: scaleY(0); transition: transform 0.55s cubic-bezier(0.5, 0, 0.3, 1); }
.toggle-shades__glint { fill: light-dark(#fbfcfd, #f1f2f3); opacity: 0.4; transform: translateX(-70px) skewX(-25deg); transition: transform 0.4s ease-in; }
.toggle-shades__input:checked + .toggle-shades__art .toggle-shades__tint { transform: none; }
.toggle-shades__input:checked + .toggle-shades__art .toggle-shades__glint { transform: translateX(110px) skewX(-25deg); transition: transform 0.7s cubic-bezier(0.4, 0, 0.6, 1) 0.45s; }
@media (prefers-reduced-motion: reduce) {
.toggle-shades__tint, .toggle-shades__glint, .toggle-shades__input:checked + .toggle-shades__art .toggle-shades__glint { transition: none; }
}
`;
const LENS = 'M10 12H50Q53 12 52 16L49 30Q47 37 39 37H22Q12 37 11 28Z';
const LENS_R = 'M112 12H72Q69 12 70 16L73 30Q75 37 83 37H100Q110 37 111 28Z';
export function ShadesToggle() {
const clip = `toggle-shades-${useId().replace(/[^a-zA-Z0-9]/g, '')}`;
return (
<>
<style>
{css}
</style>
<label className="toggle-shades">
<input className="toggle-shades__input" type="checkbox" role="switch" />
<svg className="toggle-shades__art" viewBox="0 0 122 48" aria-hidden="true">
<defs>
<linearGradient id={`${clip}-tint`} x1="0" y1="0" x2="0" y2="1">
<stop className="toggle-shades__top" offset="0" />
<stop className="toggle-shades__bottom" offset="0.75" />
</linearGradient>
<clipPath id={clip}>
<path d={LENS} />
<path d={LENS_R} />
</clipPath>
</defs>
<path className="toggle-shades__glass" d={LENS} />
<path className="toggle-shades__glass" d={LENS_R} />
<g clipPath={`url(#${clip})`}>
<circle className="toggle-shades__eye" cx="31" cy="24" r="3" />
<circle className="toggle-shades__eye" cx="91" cy="24" r="3" />
<rect className="toggle-shades__tint" fill={`url(#${clip}-tint)`} x="0" y="12" width="122" height="26" />
<path className="toggle-shades__shine" d="M17 30L24 16M80 30L87 16" />
<rect className="toggle-shades__glint" x="20" y="8" width="7" height="34" />
</g>
<path className="toggle-shades__frame" d={LENS} />
<path className="toggle-shades__frame" d={LENS_R} />
<path className="toggle-shades__frame" d="M52 16Q61 10 70 16M3 13H10M112 13H119" />
</svg>
<span>Dark mode</span>
</label>
</>
);
}Markup where the component goes, CSS in your stylesheet. No JavaScript.
HTML
<label class="toggle-shades">
<input class="toggle-shades__input" type="checkbox" role="switch"/>
<svg class="toggle-shades__art" viewBox="0 0 122 48" aria-hidden="true">
<defs>
<linearGradient id="toggle-shades-clip-tint" x1="0" y1="0" x2="0" y2="1">
<stop class="toggle-shades__top" offset="0"></stop>
<stop class="toggle-shades__bottom" offset="0.75"></stop>
</linearGradient>
<clipPath id="toggle-shades-clip">
<path d="M10 12H50Q53 12 52 16L49 30Q47 37 39 37H22Q12 37 11 28Z"></path>
<path d="M112 12H72Q69 12 70 16L73 30Q75 37 83 37H100Q110 37 111 28Z"></path>
</clipPath>
</defs>
<path class="toggle-shades__glass" d="M10 12H50Q53 12 52 16L49 30Q47 37 39 37H22Q12 37 11 28Z"></path>
<path class="toggle-shades__glass" d="M112 12H72Q69 12 70 16L73 30Q75 37 83 37H100Q110 37 111 28Z"></path>
<g clip-path="url(#toggle-shades-clip)">
<circle class="toggle-shades__eye" cx="31" cy="24" r="3"></circle>
<circle class="toggle-shades__eye" cx="91" cy="24" r="3"></circle>
<rect class="toggle-shades__tint" fill="url(#toggle-shades-clip-tint)" x="0" y="12" width="122" height="26"></rect>
<path class="toggle-shades__shine" d="M17 30L24 16M80 30L87 16"></path>
<rect class="toggle-shades__glint" x="20" y="8" width="7" height="34"></rect>
</g>
<path class="toggle-shades__frame" d="M10 12H50Q53 12 52 16L49 30Q47 37 39 37H22Q12 37 11 28Z"></path>
<path class="toggle-shades__frame" d="M112 12H72Q69 12 70 16L73 30Q75 37 83 37H100Q110 37 111 28Z"></path>
<path class="toggle-shades__frame" d="M52 16Q61 10 70 16M3 13H10M112 13H119"></path>
</svg>
<span>Dark mode</span>
</label>CSS
.toggle-shades { display: inline-flex; align-items: center; gap: 14px; color: light-dark(#42454c, #e5e6e9); font-size: 15px; cursor: pointer; user-select: none; }
.toggle-shades__input { position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0; }
.toggle-shades__art { display: block; width: 124px; height: 50px; overflow: visible; border-radius: 8px; }
.toggle-shades__input:focus-visible + .toggle-shades__art { outline: 2px solid light-dark(#5f626a, #a7abb2); outline-offset: 4px; }
.toggle-shades__glass { fill: light-dark(#dbdde1, #7d8087); }
.toggle-shades__eye { fill: light-dark(#373a40, #121417); }
.toggle-shades__frame { fill: none; stroke: light-dark(#50535b, #c6c8cd); stroke-width: 2.6; stroke-linejoin: round; stroke-linecap: round; }
/* The tint is dark in both themes. It runs down from the top like a photochromic lens. */
.toggle-shades__top { stop-color: light-dark(#50535b, #393c41); }
.toggle-shades__bottom { stop-color: light-dark(#373a40, #121417); }
.toggle-shades__shine { fill: none; stroke: light-dark(#fbfcfd, #f1f2f3); stroke-width: 1.6; stroke-linecap: round; opacity: 0; transition: opacity 0.3s; }
.toggle-shades__input:checked + .toggle-shades__art .toggle-shades__shine { opacity: 0.35; transition-delay: 0.4s; }
.toggle-shades__tint { transform-box: view-box; transform-origin: 0 12px; transform: scaleY(0); transition: transform 0.55s cubic-bezier(0.5, 0, 0.3, 1); }
.toggle-shades__glint { fill: light-dark(#fbfcfd, #f1f2f3); opacity: 0.4; transform: translateX(-70px) skewX(-25deg); transition: transform 0.4s ease-in; }
.toggle-shades__input:checked + .toggle-shades__art .toggle-shades__tint { transform: none; }
.toggle-shades__input:checked + .toggle-shades__art .toggle-shades__glint { transform: translateX(110px) skewX(-25deg); transition: transform 0.7s cubic-bezier(0.4, 0, 0.6, 1) 0.45s; }
@media (prefers-reduced-motion: reduce) {
.toggle-shades__tint, .toggle-shades__glint, .toggle-shades__input:checked + .toggle-shades__art .toggle-shades__glint { transition: none; }
}Goal: Create a "Shades" toggle switch.
Visual behavior: A dark mode switch drawn as sunglasses: the lenses tint from the top down like photochromic glass and a glint slides across them.
Technique:
- A native checkbox with role="switch", visually hidden but focusable, sits inside a <label> before an aria-hidden drawing and the visible text. Every on state is styled from the input's :checked state through the adjacent sibling combinator, so Space and clicks both work.
- Each lens is clipped to its shape and holds a vertical gradient tint that scales down from the top, so it darkens downwards. The tint stays dark in both themes.
- A glint sweeps across the lenses as they darken, and two fixed shine lines keep the glass reading as glass. Pure CSS.
Accessibility: A native checkbox with role="switch" inside a <label>, so Space toggles it and the visible text names it; :focus-visible on the input outlines the drawing, which is aria-hidden. Under prefers-reduced-motion the lenses tint at once.
My stack: {{USER_STACK}}
My styling: {{USER_STYLING}}
My constraints: {{USER_CONSTRAINTS}}
My colors: neutral only. Black, white and grays, no hues. Use the color values above as written, or my project's closest neutral tokens.
Return a single self-contained component. No dependencies.Goal: Create a "Shades" toggle switch.
Visual behavior: A dark mode switch drawn as sunglasses: the lenses tint from the top down like photochromic glass and a glint slides across them.
Technique:
- A native checkbox with role="switch", visually hidden but focusable, sits inside a <label> before an aria-hidden drawing and the visible text. Every on state is styled from the input's :checked state through the adjacent sibling combinator, so Space and clicks both work.
- Each lens is clipped to its shape and holds a vertical gradient tint that scales down from the top, so it darkens downwards. The tint stays dark in both themes.
- A glint sweeps across the lenses as they darken, and two fixed shine lines keep the glass reading as glass. Pure CSS.
Accessibility: A native checkbox with role="switch" inside a <label>, so Space toggles it and the visible text names it; :focus-visible on the input outlines the drawing, which is aria-hidden. Under prefers-reduced-motion the lenses tint at once.
My stack: {{USER_STACK}}
My styling: {{USER_STYLING}}
My constraints: {{USER_CONSTRAINTS}}
My colors: use my project's existing color tokens. The color values above are grayscale placeholders. Map each one by its role (surface, text, border, accent, glow) to my theme, and keep their relative lightness so contrast and the effect still read.
Return a single self-contained component. No dependencies.Paste into Claude, ChatGPT or Cursor after filling in {{USER_STACK}}, {{USER_STYLING}} and {{USER_CONSTRAINTS}}. Neutral keeps the greys you see here; Match my project asks the model to map them onto your own colour tokens.
Free to use in any project, commercial included. MIT-0, no attribution required. Licence
About this component
A dark mode switch drawn as sunglasses: the lenses tint from the top down like photochromic glass and a glint slides across them.
- Dependencies
- None
- Difficulty
- Intermediate
- Triggers
- click, state change, gradient
- Techniques
- svg, gradient, css transition
- Reduced motion
- Respected
- Theme
- Light and dark (follows your page's color-scheme)
A native checkbox with role="switch" inside a <label>, so Space toggles it and the visible text names it; :focus-visible on the input outlines the drawing, which is aria-hidden. Under prefers-reduced-motion the lenses tint at once.