Gate

No dependencies
Code format
React + Tailwind
const css = `
  .toggle-gate { display: inline-flex; align-items: center; gap: 14px; color: light-dark(#42454c, #e5e6e9); font-size: 15px; cursor: pointer; user-select: none; }
  .toggle-gate__input { position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0; }
  .toggle-gate__art { display: block; width: 136px; height: 66px; overflow: visible; border-radius: 8px; }
  .toggle-gate__input:focus-visible + .toggle-gate__art { outline: 2px solid light-dark(#5f626a, #a7abb2); outline-offset: 4px; }
  .toggle-gate__road { fill: light-dark(#dbdde1, #2e3035); }
  .toggle-gate__line { fill: light-dark(#ebedf0, #232529); }
  .toggle-gate__post { fill: light-dark(#dbdde1, #2e3035); }
  .toggle-gate__box { fill: light-dark(#cbced3, #393c41); }
  .toggle-gate__fork { fill: light-dark(#cbced3, #393c41); }
  .toggle-gate__hub { fill: light-dark(#5f626a, #a7abb2); }
  .toggle-gate__weight { fill: light-dark(#6d7179, #7d8087); }
  .toggle-gate__boom { fill: light-dark(#373a40, #f1f2f3); }
  .toggle-gate__stripe { fill: light-dark(#6d7179, #7d8087); }
  .toggle-gate__lamp { fill: light-dark(#cbced3, #393c41); transition: fill 0.25s, filter 0.25s; }
  /* Down, the arm drops onto its rest and gives a little; up, it swings past and settles back. */
  .toggle-gate__arm { transform-box: view-box; transform-origin: 15px 26px; transition: transform 0.6s cubic-bezier(0.55, 0, 0.6, 1.25); }
  .toggle-gate__input:checked + .toggle-gate__art .toggle-gate__arm { transform: rotate(-80deg); transition: transform 0.9s cubic-bezier(0.3, 1.35, 0.55, 1); }
  .toggle-gate__input:checked + .toggle-gate__art .toggle-gate__lamp { fill: light-dark(#373a40, #f1f2f3); filter: drop-shadow(0 0 3px light-dark(#373a40, #f1f2f3)); transition-delay: 0.35s; }
  @media (prefers-reduced-motion: reduce) {
    .toggle-gate__arm, .toggle-gate__input:checked + .toggle-gate__art .toggle-gate__arm { transition: none; }
  }
`;

export function GateToggle() {
  return (
    <>
      <style>
        {css}
      </style>
      <label className="toggle-gate">
        <input className="toggle-gate__input" type="checkbox" role="switch" />
        <svg className="toggle-gate__art" viewBox="0 0 136 66" aria-hidden="true">
          <rect className="toggle-gate__road" x="0" y="58" width="136" height="4" rx="2" />
          {[36, 60, 84, 108].map((x) => (
            <rect key={x} className="toggle-gate__line" x={x} y="59.5" width="12" height="1" />
          ))}
          <path className="toggle-gate__fork" d="M124 58V35H122V32H129V35H127V58Z" />
          <rect className="toggle-gate__post" x="8" y="26" width="14" height="32" rx="2" />
          <g className="toggle-gate__arm">
            <rect className="toggle-gate__weight" x="1" y="22.5" width="11" height="7" rx="1.5" />
            <rect className="toggle-gate__boom" x="15" y="23.5" width="113" height="5" rx="2.5" />
            {[24, 48, 72, 96, 120].map((x) => (
              <rect key={x} className="toggle-gate__stripe" x={x} y="23.5" width="12" height="5" />
            ))}
          </g>
          <rect className="toggle-gate__box" x="6" y="20" width="18" height="12" rx="3" />
          <circle className="toggle-gate__hub" cx="15" cy="26" r="3" />
          <rect className="toggle-gate__box" x="12.5" y="14" width="5" height="6" />
          <circle className="toggle-gate__lamp" cx="15" cy="11" r="4" />
        </svg>
        <span>Guest access</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-gate
Source on GitHub

About this component

A guest access switch drawn as a barrier gate: the striped arm swings up past upright and settles back as the lamp on the post lights; off drops it onto its rest with a little give.

Dependencies
None
Difficulty
Beginner
Triggers
click, state change, spring
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 the arm moves at once.

More toggles

See all
Handbrake
Jack
Kettle
Lights