Signature Library · Indian
Auto Meter
Rolling fare drums in a steel box. Drop the flag and the fare starts to climb.
States
Default
Hover
Active
Focus
Code
auto-meter.html
<div class="sig-auto-meter" data-start="30">
<span class="sig-auto-meter__case">
<span class="sig-auto-meter__top">
<span class="sig-auto-meter__name">Fare</span>
<span class="sig-auto-meter__lamp" aria-hidden="true"></span>
</span>
<span class="sig-auto-meter__face" role="img" aria-label="Fare" data-face>
<span class="sig-auto-meter__sign" aria-hidden="true">₹</span>
<span class="sig-auto-meter__drum">
<span class="sig-auto-meter__strip" data-strip>0123456789</span>
</span>
<span class="sig-auto-meter__drum">
<span class="sig-auto-meter__strip" data-strip>0123456789</span>
</span>
<span class="sig-auto-meter__drum">
<span class="sig-auto-meter__strip" data-strip>0123456789</span>
</span>
<span class="sig-auto-meter__dot" aria-hidden="true"></span>
<span class="sig-auto-meter__paise">
<span class="sig-auto-meter__strip" data-strip>0123456789</span>
</span>
<span class="sig-auto-meter__paise">
<span class="sig-auto-meter__strip" data-strip>0123456789</span>
</span>
</span>
</span>
<button class="sig-auto-meter__lever" type="button" aria-pressed="false" aria-label="Start or stop the meter" data-flag>
<span class="sig-auto-meter__flag">For hire</span>
</button>
</div>
auto-meter.css
.sig-auto-meter {
--sig-case: #1e1e24;
--sig-digit: #f4f1e6;
--sig-paise: #ff5a4d;
--sig-flag: #d3312f;
--sig-speed: 320ms;
}
.sig-auto-meter {
position: relative;
box-sizing: border-box;
display: flex;
align-items: flex-end;
gap: 4px;
font-family: ui-monospace, Consolas, monospace;
--tilt: 0deg;
--glow: #3a1414;
}
.sig-auto-meter[data-state=run] {
--tilt: -90deg;
--glow: #ff4d4d;
}
.sig-auto-meter__case {
display: grid;
gap: 8px;
padding: 10px 12px 14px;
background: linear-gradient(165deg, color-mix(in srgb, var(--sig-case) 82%, #fff), var(--sig-case) 45%);
border: 3px solid #8d8f96;
border-radius: 10px 10px 4px 4px;
box-shadow: 0 8px 0 #0a0a0d, 0 14px 18px rgba(0,0,0,.45);
}
.sig-auto-meter__top {
display: flex;
justify-content: space-between;
align-items: center;
color: #c6c7cc;
font-size: 10px;
letter-spacing: 0.16em;
text-transform: uppercase;
}
.sig-auto-meter__lamp {
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--glow);
box-shadow: 0 0 8px var(--glow);
transition: background 200ms ease;
}
.sig-auto-meter__face {
display: flex;
align-items: center;
gap: 2px;
padding: 6px 8px;
background: #060608;
border-radius: 4px;
box-shadow: inset 0 2px 6px #000;
}
.sig-auto-meter__sign {
margin-right: 4px;
color: var(--sig-digit);
font-size: 16px;
}
.sig-auto-meter__dot {
width: 4px;
height: 4px;
margin: 16px 1px 0;
border-radius: 50%;
background: var(--sig-digit);
}
.sig-auto-meter__drum {
display: block;
width: 22px;
height: 34px;
overflow: hidden;
color: var(--sig-digit);
font-size: 22px;
text-align: center;
background: linear-gradient(180deg, #000, #2c2c33 45%, #2c2c33 55%, #000);
}
.sig-auto-meter__paise {
display: block;
width: 22px;
height: 34px;
overflow: hidden;
color: var(--sig-paise);
font-size: 22px;
text-align: center;
background: linear-gradient(180deg, #000, #2c2c33 45%, #2c2c33 55%, #000);
}
.sig-auto-meter__strip {
display: block;
width: 1ch;
margin: 0 auto;
line-height: 34px;
word-break: break-all;
transform: translateY(calc(var(--n, 0) * -34px));
transition: transform var(--sig-speed) ease;
}
.sig-auto-meter__lever {
position: relative;
width: 26px;
height: 96px;
padding: 0;
color: inherit;
background: linear-gradient(90deg, #6f7178, #b9bbc2, #6f7178) 50% 100% / 6px 100% no-repeat;
border: 0;
cursor: pointer;
transform: rotate(var(--tilt));
transform-origin: 50% 100%;
transition: transform var(--sig-speed) ease;
}
.sig-auto-meter__lever:focus-visible {
outline: 2px solid var(--sig-flag);
outline-offset: 2px;
}
.sig-auto-meter__flag {
position: absolute;
top: 0;
left: 0;
display: grid;
place-items: center;
width: 26px;
height: 62px;
color: #fff;
font-size: 9px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
writing-mode: vertical-rl;
background: var(--sig-flag);
border-radius: 3px;
}
@media (prefers-reduced-motion: reduce) {
.sig-auto-meter__lamp,
.sig-auto-meter__strip,
.sig-auto-meter__lever {
animation: none !important;
transition: none !important;
}
}
auto-meter.js
document.querySelectorAll(".sig-auto-meter").forEach((el) => {
const strips = Array.from(el.querySelectorAll("[data-strip]"));
const lever = el.querySelector("[data-flag]");
const face = el.querySelector("[data-face]");
const start = Number(el.dataset.start);
let fare = start;
let timer = 0;
const show = () => {
const digits = String(Math.round(fare * 100)).padStart(5, "0");
strips.forEach((s, i) => s.style.setProperty("--n", digits[i]));
face.setAttribute("aria-label", "Fare " + fare.toFixed(2) + " rupees");
};
const flip = () => {
const run = lever.getAttribute("aria-pressed") !== "true";
lever.setAttribute("aria-pressed", String(run));
window.clearInterval(timer);
if (run) {
el.dataset.state = "run";
fare = start;
show();
timer = window.setInterval(() => {
fare += 0.5;
show();
}, 700);
} else {
el.removeAttribute("data-state");
}
};
show();
lever.addEventListener("click", flip);
return () => {
window.clearInterval(timer);
lever.removeEventListener("click", flip);
};
});
auto-meter.tsx
import { useEffect, useRef } from "react";
import type React from "react";
import "./auto-meter.css"; // the CSS tab
function attach(el: HTMLElement) {
const strips = Array.from(el.querySelectorAll("[data-strip]")) as HTMLElement[];
const lever = el.querySelector("[data-flag]") as HTMLElement;
const face = el.querySelector("[data-face]") as HTMLElement;
const start = Number(el.dataset.start);
let fare = start;
let timer = 0;
const show = () => {
const digits = String(Math.round(fare * 100)).padStart(5, "0");
strips.forEach((s, i) => s.style.setProperty("--n", digits[i]));
face.setAttribute("aria-label", "Fare " + fare.toFixed(2) + " rupees");
};
const flip = () => {
const run = lever.getAttribute("aria-pressed") !== "true";
lever.setAttribute("aria-pressed", String(run));
window.clearInterval(timer);
if (run) {
el.dataset.state = "run";
fare = start;
show();
timer = window.setInterval(() => {
fare += 0.5;
show();
}, 700);
} else {
el.removeAttribute("data-state");
}
};
show();
lever.addEventListener("click", flip);
return () => {
window.clearInterval(timer);
lever.removeEventListener("click", flip);
};
}
export function SigAutoMeter({ children = "Fare", ...props }: React.ComponentProps<"div">) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => attach(ref.current!), []);
return (
<div className="sig-auto-meter" data-start="30" ref={ref} {...props}>
<span className="sig-auto-meter__case">
<span className="sig-auto-meter__top">
<span className="sig-auto-meter__name">{children}</span>
<span className="sig-auto-meter__lamp" aria-hidden="true" />
</span>
<span className="sig-auto-meter__face" role="img" aria-label="Fare" data-face>
<span className="sig-auto-meter__sign" aria-hidden="true">{"₹"}</span>
<span className="sig-auto-meter__drum">
<span className="sig-auto-meter__strip" data-strip>{"0123456789"}</span>
</span>
<span className="sig-auto-meter__drum">
<span className="sig-auto-meter__strip" data-strip>{"0123456789"}</span>
</span>
<span className="sig-auto-meter__drum">
<span className="sig-auto-meter__strip" data-strip>{"0123456789"}</span>
</span>
<span className="sig-auto-meter__dot" aria-hidden="true" />
<span className="sig-auto-meter__paise">
<span className="sig-auto-meter__strip" data-strip>{"0123456789"}</span>
</span>
<span className="sig-auto-meter__paise">
<span className="sig-auto-meter__strip" data-strip>{"0123456789"}</span>
</span>
</span>
</span>
<button className="sig-auto-meter__lever" type="button" aria-pressed="false" aria-label="Start or stop the meter" data-flag>
<span className="sig-auto-meter__flag">{"For hire"}</span>
</button>
</div>
);
}
auto-meter.html
<div class="sig-auto-meter relative [box-sizing:border-box] [display:flex] [align-items:flex-end] gap-[4px] [font-family:ui-monospace,_Consolas,_monospace] [--tilt:0deg] [--glow:#3a1414] [&[data-state=run]]:[--tilt:-90deg] [&[data-state=run]]:[--glow:#ff4d4d]" data-start="30">
<span class="grid gap-[8px] [padding:10px_12px_14px] [background:linear-gradient(165deg,_color-mix(in_srgb,_#1e1e24_82%,_#fff),_#1e1e24_45%)] [border:3px_solid_#8d8f96] rounded-[10px_10px_4px_4px] [box-shadow:0_8px_0_#0a0a0d,_0_14px_18px_rgba(0,0,0,.45)]">
<span class="[display:flex] [justify-content:space-between] items-center text-[#c6c7cc] text-[10px] [letter-spacing:0.16em] [text-transform:uppercase]">
<span>Fare</span>
<span class="w-[10px] h-[10px] rounded-[50%] [background:var(--glow)] [box-shadow:0_0_8px_var(--glow)] [transition:background_200ms_ease] motion-reduce:[transition:none!important]" aria-hidden="true"></span>
</span>
<span class="[display:flex] items-center gap-[2px] [padding:6px_8px] bg-[#060608] rounded-[4px] [box-shadow:inset_0_2px_6px_#000]" role="img" aria-label="Fare" data-face>
<span class="[margin-right:4px] text-[#f4f1e6] text-[16px]" aria-hidden="true">₹</span>
<span class="[display:block] w-[22px] h-[34px] overflow-hidden text-[#f4f1e6] text-[22px] [text-align:center] [background:linear-gradient(180deg,_#000,_#2c2c33_45%,_#2c2c33_55%,_#000)]">
<span class="[display:block] w-[1ch] [margin:0_auto] [line-height:34px] [word-break:break-all] [transform:translateY(calc(var(--n,_0)_*_-34px))] [transition:transform_320ms_ease] motion-reduce:[transition:none!important]" data-strip>0123456789</span>
</span>
<span class="[display:block] w-[22px] h-[34px] overflow-hidden text-[#f4f1e6] text-[22px] [text-align:center] [background:linear-gradient(180deg,_#000,_#2c2c33_45%,_#2c2c33_55%,_#000)]">
<span class="[display:block] w-[1ch] [margin:0_auto] [line-height:34px] [word-break:break-all] [transform:translateY(calc(var(--n,_0)_*_-34px))] [transition:transform_320ms_ease] motion-reduce:[transition:none!important]" data-strip>0123456789</span>
</span>
<span class="[display:block] w-[22px] h-[34px] overflow-hidden text-[#f4f1e6] text-[22px] [text-align:center] [background:linear-gradient(180deg,_#000,_#2c2c33_45%,_#2c2c33_55%,_#000)]">
<span class="[display:block] w-[1ch] [margin:0_auto] [line-height:34px] [word-break:break-all] [transform:translateY(calc(var(--n,_0)_*_-34px))] [transition:transform_320ms_ease] motion-reduce:[transition:none!important]" data-strip>0123456789</span>
</span>
<span class="w-[4px] h-[4px] [margin:16px_1px_0] rounded-[50%] bg-[#f4f1e6]" aria-hidden="true"></span>
<span class="[display:block] w-[22px] h-[34px] overflow-hidden text-[#ff5a4d] text-[22px] [text-align:center] [background:linear-gradient(180deg,_#000,_#2c2c33_45%,_#2c2c33_55%,_#000)]">
<span class="[display:block] w-[1ch] [margin:0_auto] [line-height:34px] [word-break:break-all] [transform:translateY(calc(var(--n,_0)_*_-34px))] [transition:transform_320ms_ease] motion-reduce:[transition:none!important]" data-strip>0123456789</span>
</span>
<span class="[display:block] w-[22px] h-[34px] overflow-hidden text-[#ff5a4d] text-[22px] [text-align:center] [background:linear-gradient(180deg,_#000,_#2c2c33_45%,_#2c2c33_55%,_#000)]">
<span class="[display:block] w-[1ch] [margin:0_auto] [line-height:34px] [word-break:break-all] [transform:translateY(calc(var(--n,_0)_*_-34px))] [transition:transform_320ms_ease] motion-reduce:[transition:none!important]" data-strip>0123456789</span>
</span>
</span>
</span>
<button class="relative w-[26px] h-[96px] [padding:0] [color:inherit] [background:linear-gradient(90deg,_#6f7178,_#b9bbc2,_#6f7178)_50%_100%_/_6px_100%_no-repeat] [border:0] cursor-pointer [transform:rotate(var(--tilt))] [transform-origin:50%_100%] [transition:transform_320ms_ease] focus-visible:[outline:2px_solid_#d3312f] focus-visible:[outline-offset:2px] motion-reduce:[transition:none!important]" type="button" aria-pressed="false" aria-label="Start or stop the meter" data-flag>
<span class="absolute [top:0] [left:0] grid [place-items:center] w-[26px] h-[62px] text-[#fff] text-[9px] font-[700] [letter-spacing:0.08em] [text-transform:uppercase] [writing-mode:vertical-rl] bg-[#d3312f] rounded-[3px]">For hire</span>
</button>
</div>
<!-- Needs the script from the JS tab. -->