steady-counter is a small, calm counter application. Its product intent is singular and complete: a person opens the app, sees a current count value, and increases, decreases, or resets that count. There is no account, no list of counters, no history, and no reporting — the count itself is the whole product.
The audience is one person counting items who wants a frictionless tool that behaves like a real object: a large readable number and three tactile controls. The design language follows Naoto Fukasawa's "super normal" idea — soft paper-white ground, ink text, a terracotta accent used only on the active count and one hairline rule, and controls that press rather than animate.
This document is the System Requirements Document for steady-counter. It defines the two accepted surfaces (Landing and Counter), the single accepted persona (Counter user), the four accepted counter behaviors, and the visual, interaction, and technical constraints that make them buildable.
steady-counter is a single-purpose web application delivered as a React frontend, a shared FastAPI backend, and a MariaDB database provisioned by the initial scaffold. The product behavior is entirely client-facing: the Counter page displays a count value and provides increase, decrease, and reset controls. The Landing page introduces the app and routes the user into counting.
The only active human actor is the Counter user, who counts items with one counter. There is no authentication, no account, and no differentiated permission model — both pages are publicly reachable (access_requirement: none), and the count is a single shared value within the user's session.
Current delivery includes:
Narrow exclusions: no multiple counters, no named counters, no count history or undo log, no sharing, no accounts, no analytics dashboards, no settings beyond the counter controls themselves.
Removed from scope
steady-counter is a first-party, application-owned utility. Both accepted pages are owned by the application and are reachable without identity: the Landing page is a public entry surface, and the Counter page is a public working surface. Because the accepted behavior creates no durable actor-specific state, no commitment, and no value transfer that must remain bound to a participant, no application-owned identity, sign-in, or account lifecycle is introduced. The count lives for the duration of the user's use of the Counter page.
The current delivery horizon covers the Landing and Counter pages and the four accepted counter behaviors. There is no accepted future horizon in the requirement thread; anything beyond the counter (multiple counters, persistence across sessions, sharing, history) is out of scope and is not planned here.
The backend and database are provisioned infrastructure. They do not, by themselves, create product requirements, domain entities, or authentication. The counter's observable behavior is delivered through the frontend UI; the backend remains available for application APIs and domain modules if the accepted behavior requires them, and the database remains available for application-owned persistent data without being required to store counter state.
Not applicable. No reference directive with content_source was supplied.
clamp(96px, 22vw, 240px). A terracotta (#B4552F) hairline rule sits above the numeral and marks the panel top and nothing else. The count is the single source of truth for the page.FR-1 — Display the current count (explicit) As a Counter user, I should see the current count value so that I know how many items I have counted.
clamp(96px, 22vw, 240px), tabular figures, ink on #FCFBF8, with the terracotta hairline above it.FR-2 — Increase the count (explicit) As a Counter user, I should increase the count so that I can tally an additional item.
FR-3 — Decrease the count (explicit) As a Counter user, I should decrease the count so that I can correct an over-count.
FR-4 — Reset the count (explicit) As a Counter user, I should reset the count so that I can start a fresh tally.
FR-5 — Open the app and start counting (required_inference) As a Counter user, I should open the app and reach the counter so that I can begin counting.
The creative direction is authoritative: Super normal counting after Naoto Fukasawa.
#F4F2ED (soft paper white ground)#FCFBF8 (count panel and still-life panel)#25231F (ink)#25231F (ink buttons)#B4552F (terracotta — active count and one hairline rule only)#8C877D (secondary labels)clamp(96px, 22vw, 240px).The signature concept is the count as a real object. On the public entry (Landing), a full-width paper ground (#F4F2ED) holds one large square still-life panel in the center of a wide-margin centered column. The panel is soft white (#FCFBF8) with a 20px radius and contains one soft-lit still life of a plain hand tally object on a paper ground — no stock people, no clip art. The "steady-counter" wordmark sits above the panel in Zen Kaku Gothic New Medium with wide tracking on the small label. A single capsule entry control sits below the panel, styled as an ink (#25231F) capsule with a 1px inner edge. The composition is flat, composed, and still: nothing performs except the tactile press of the entry control. The same column, panel, and capsule language carry directly into the Counter page, where the count numeral becomes the hero.
Interaction Model: Static (direction) Motion Tempo: still Hero Dimensionality: flat
Landing Hero Motion Brief
prefers-reduced-motion, the 120ms scale press is removed; the count numeral updates instantly and the capsule press-in is suppressed. The static arrangement remains fully usable and every readable element stays whole.font-size: clamp(...) with its mobile size) to fit. No other element covers any part of them. Imagery and decoration may be cropped or bled off an edge as the direction asks, as long as they cover no readable text or control.prefers-reduced-motion, provide a usable static arrangement; the 120ms scale press and 2px capsule press-in are suppressed and the count updates instantly.#F4F2ED, surface #FCFBF8, text #25231F, primary #25231F, accent #B4552F, muted #8C877D. The terracotta accent appears only on the active count and one hairline rule. No blue or indigo accents; no gradient blobs or glass panels; no hover-lift card grids; no bouncy or springy motion.clamp(96px, 22vw, 240px).access_requirement: none; no sign-in, account, or role-based visibility is introduced.backend, database, frontend.access_requirement: none; no authentication, account, or permission model is introduced.backend, database, frontend) are authoritative infrastructure; the shared backend is reused for feature APIs, and the provisioned database does not justify domain entities, authentication, or an ER diagram.#F4F2ED on which the centered column and panels sit.#B4552F hairline rule above the count numeral that marks the panel top and nothing else.
A calm counter
One number and three controls for counting items, nothing else.

A calm counter
One number and three controls for counting items, nothing else.
No comments yet. Be the first!