Shell

No dependencies
Code format
React + Tailwind
const css = `
  .toggle-shell { display: inline-flex; align-items: center; gap: 14px; color: light-dark(#42454c, #e5e6e9); font-size: 15px; cursor: pointer; user-select: none; }
  .toggle-shell__input { position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0; }
  .toggle-shell__art { display: block; width: 112px; height: 68px; overflow: visible; border-radius: 8px; }
  .toggle-shell__input:focus-visible + .toggle-shell__art { outline: 2px solid light-dark(#5f626a, #a7abb2); outline-offset: 4px; }
  .toggle-shell__ground { fill: light-dark(#dbdde1, #2e3035); }
  .toggle-shell__skin { fill: light-dark(#5f626a, #a7abb2); }
  .toggle-shell__eye { fill: light-dark(#f3f4f7, #1a1c1f); }
  .toggle-shell__dome { fill: light-dark(#6d7179, #7d8087); }
  .toggle-shell__rim { fill: light-dark(#cbced3, #393c41); }
  .toggle-shell__scute { fill: none; stroke: light-dark(#cbced3, #393c41); stroke-width: 1.4; stroke-linejoin: round; }
  /* Coming out: the shell lifts, the head eases out, then the legs. Going in it all happens the other way round, faster. */
  .toggle-shell__head { transition: transform 0.6s cubic-bezier(0.3, 1.3, 0.5, 1) 0.25s; }
  .toggle-shell__leg { transition: transform 0.35s ease-out 0.4s; }
  .toggle-shell__tail { transition: transform 0.35s ease-out 0.4s; }
  .toggle-shell__body { transition: transform 0.3s ease-out; }
  .toggle-shell__input:checked + .toggle-shell__art .toggle-shell__head { transform: translateX(-17px); transition: transform 0.25s cubic-bezier(0.6, 0, 0.8, 0.4) 0.05s; }
  .toggle-shell__input:checked + .toggle-shell__art .toggle-shell__leg { transform: translateY(-9px); transition: transform 0.2s ease-in; }
  .toggle-shell__input:checked + .toggle-shell__art .toggle-shell__tail { transform: translate(7px, -2px); transition: transform 0.2s ease-in; }
  .toggle-shell__input:checked + .toggle-shell__art .toggle-shell__body { transform: translateY(5px); transition: transform 0.3s cubic-bezier(0.5, 0, 0.6, 1.4) 0.2s; }
  @media (prefers-reduced-motion: reduce) {
    .toggle-shell__head, .toggle-shell__leg, .toggle-shell__tail, .toggle-shell__body,
    .toggle-shell__input:checked + .toggle-shell__art .toggle-shell__head,
    .toggle-shell__input:checked + .toggle-shell__art .toggle-shell__leg,
    .toggle-shell__input:checked + .toggle-shell__art .toggle-shell__tail,
    .toggle-shell__input:checked + .toggle-shell__art .toggle-shell__body { transition: none; }
  }
`;

export function ShellToggle() {
  return (
    <>
      <style>
        {css}
      </style>
      <label className="toggle-shell">
        <input className="toggle-shell__input" type="checkbox" role="switch" />
        <svg className="toggle-shell__art" viewBox="0 0 108 66" aria-hidden="true">
          <rect className="toggle-shell__ground" x="4" y="61" width="100" height="3" rx="1.5" />
          <g className="toggle-shell__body">
            {/* Everything that tucks away is drawn first, so the shell covers it as it goes in. */}
            <g className="toggle-shell__head">
              <path className="toggle-shell__skin" d="M74 42Q84 30 93 33Q101 36 98 43Q95 48 86 47H74Z" />
              <circle className="toggle-shell__eye" cx="92" cy="38" r="1.4" />
            </g>
            <path className="toggle-shell__skin toggle-shell__tail" d="M28 46L17 49L28 50Z" />
            <rect className="toggle-shell__skin toggle-shell__leg" x="33" y="44" width="9" height="17" rx="4" />
            <rect className="toggle-shell__skin toggle-shell__leg" x="64" y="44" width="9" height="17" rx="4" />
            <path className="toggle-shell__dome" d="M26 47C26 20 80 20 82 47Z" />
            <path className="toggle-shell__scute" d="M36 46L40 34L54 29L68 34L72 46M40 34L32 40M68 34L77 40M54 29V22M40 34L48 46M68 34L60 46" />
            <rect className="toggle-shell__rim" x="23" y="45" width="62" height="5" rx="2.5" />
          </g>
        </svg>
        <span>Private</span>
      </label>
    </>
  );
}

Free to use in any project, commercial included. MIT-0, no attribution required. Licence

Install with shadcn

npx shadcn@latest add @movin/toggle-shell
Source on GitHub

About this component

A private switch drawn as a turtle: on, it pulls its head, legs and tail into its shell and settles down; off, the shell lifts and it peeks back out.

Dependencies
None
Difficulty
Beginner
Triggers
click, state change
Techniques
css transition, svg, transform only
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 it tucks in at once.

More toggles

See all
Sign
Signal
Turntable
Valve