{"slug":"appetite","name":"Appetite","version":"1.3.4","category":"dashboard","priceCents":3900,"description":"Appetite is a Next.js and Tailwind CSS shape up project management dashboard template for teams that run six-week cycles and decide what they will not build. Eight routes cover the running cycle, the hill chart, a scope, the pitch queue, a shaped pitch, the betting table, workspace settings and a living component guide. The signature is the hill track: one curve computed from the scope fixture, drawn full-bleed as the cycle ridge, inline in every scope row, and as a single marker on a card, so the drawing can never disagree with the table beside it. The shell ships three navigation geometries behind one slot contract — a labelled sidebar, a 72px icon rail and a horizontal top bar — picked from the header and remembered per reader, because a six-week cycle is watched from a different posture in week 1 than in week 5; both editions implement it, React through its grid and the HTML edition through one attribute and a pre-paint script. The theme carries the catalog's first reusable overlay layer, written without an overlay library: dialog, typed confirmation, popover, dropdown menu, tooltip, command palette, date picker and calendar, each with Escape to close, a focus trap on modal layers, focus returned to the trigger, and the aria state the trigger owes. Beside it sit the disclosure parts the console reads with: a tablist with the full arrow-key model, a disclosure region, a semantic progress bar, a non-animated skeleton paired with a spoken loading line, and a transient message region held to one rule, that a toast never carries the only copy of a fact. Its primitive state layer ships label, description, invalid, accepted, read-only, disabled and submitting treatments over a dedicated --field-border token held to the soft band, so a recessed field surface and its label carry identification and the rest border never reads louder than hover, focus or the selected state. Working local behaviour covers search, removable filter chips, sorting with aria-sort, pagination, keyboard and pointer reorder with position announcements, a hill slider with the full arrow-key model, form validation with a focused error summary, an over-capacity block, a read-only observer projection, and two irreversible actions behind typed confirmations. The Tidemark fiction follows one product team through cycle 24: four bets, eleven scopes, forty-three tasks and a betting table for cycle 25, with every figure on every screen derived from one fixture spine. Actions are honest local previews; persistence, auth and real cycle data are buyer integration points. Light mode is a cool near-white console on white cards; dark mode is the same system on a Linear-style near-black. Familjen Grotesk, Geist and Geist Mono ship locally, every colour routes through 109 semantic tokens across two modes, both modes meet computed WCAG AA, and motion respects reduced-motion preferences. All eight routes also ship as a framework-free HTML edition, compiled to standalone documents with local fonts and vanilla JavaScript, with no build step between the download and a static host.","summary":"For teams running six-week cycles: a shape up project management dashboard template with pitches, a betting table and hill charts. 8 routes, HTML and React.","tags":["shape up project management dashboard template","betting table ui template","hill chart react dashboard","product cycle planning template","tailwind react overlay components","tailwind-v4"],"palette":{"bg":"#f3f4f7","surface":"#ffffff","accent":"#058f8f","text":"#222326","line":"#d0d3dc"},"lead":{"route":"cycle","mode":"light"},"darkMode":true,"pages":[{"path":"/","title":"Cycle","sections":["app-shell","bet-index","attention-queue","cycle-shape"]},{"path":"/pitches","title":"Pitches","sections":["app-shell","pitch-queue","shaping-lane"]},{"path":"/pitch","title":"Pitch","sections":["app-shell","pitch-document","pitch-decision"]},{"path":"/betting-table","title":"Betting table","sections":["app-shell","betting-table"]},{"path":"/cycle","title":"Hill chart","sections":["app-shell","hill-ridge","appetite-burn","circuit-breaker","scope-ledger"]},{"path":"/scope","title":"Scope","sections":["app-shell","scope-hill","scope-tasks","scope-history"]},{"path":"/settings","title":"Settings","sections":["app-shell","cycle-settings","team-settings"]},{"path":"/components","title":"Components","sections":["app-shell","ui","dashboard-layout","overlay-gallery","bet-index","attention-queue","cycle-shape","pitch-queue","shaping-lane","pitch-document","pitch-decision","betting-table","hill-ridge","appetite-burn","circuit-breaker","scope-ledger","scope-hill","scope-tasks","scope-history","cycle-settings","team-settings"]}],"components":[{"name":"app-shell","file":"src/components/app-shell.tsx","renders":"Product shell composed through the theme-local dashboard-layout boundary: the full-width cycle bar, a 232px labelled rail, responsive disclosure, breadcrumb, command bar, page heading and footer."},{"name":"bet-index","file":"src/components/cycle-overview.tsx","renders":"The home route's opening instrument: four bets enumerated with appetite wells, per-scope hill tracks and owners, with the cycle standing as the one oversized figure."},{"name":"attention-queue","file":"src/components/cycle-overview.tsx","renders":"Derived attention list for stalled scopes, scopes still uphill late in the cycle, and uncommitted capacity on the next table."},{"name":"cycle-shape","file":"src/components/cycle-overview.tsx","renders":"Three counted readings of where the work sits: on the hill, not started, shipped."},{"name":"pitch-queue","file":"src/components/pitch-queue.tsx","renders":"Searchable, filterable, sortable pitch register with removable filter chips, a preserved no-match state, 25-row pagination, a per-row dropdown menu whose reversible actions raise a transient message with an undo, and an archive filter that takes the table past its page boundary."},{"name":"shaping-lane","file":"src/components/pitch-queue.tsx","renders":"Pitches without a settled appetite, kept out of the betting table until they have one."},{"name":"pitch-document","file":"src/components/pitch-document.tsx","renders":"The pitch as a reading surface: problem, appetite at display size, a three-place breadboard, named rabbit holes and written no-gos."},{"name":"pitch-decision","file":"src/components/pitch-decision.tsx","renders":"The bet decision: a dialog carrying team, start date and reason, a menu of reversible alternatives, a durable on-page receipt and an undo."},{"name":"betting-table","file":"src/components/betting-table.tsx","renders":"The capacity decision surface: keyboard and pointer reorder with position announcements, per-row bet toggles that recompute committed team-weeks, an over-capacity block, a read-only observer projection and the decision log."},{"name":"hill-ridge","file":"src/components/cycle-hill.tsx","renders":"The signature at full size: one hill curve breaking the page container, carrying a labelled marker for every scope in the cycle."},{"name":"scope-ledger","file":"src/components/cycle-hill.tsx","renders":"Every scope with its inline hill track, exact position, task count, owner and last movement."},{"name":"appetite-burn","file":"src/components/appetite-burn.tsx","renders":"Recharts figure: team-weeks spent against a fixed appetite per bet, with a dashed appetite outline, direct labels, a skeleton for the pre-mount hole, a plain-language summary and an exact data table."},{"name":"circuit-breaker","file":"src/components/circuit-breaker.tsx","renders":"Derived risk list with the irreversible break action behind a typed confirmation, a disclosure explaining the threshold in force, plus the stalled-scope reading."},{"name":"scope-hill","file":"src/components/scope-detail.tsx","renders":"The hill control: pointer drag and a full keyboard slider model over the same curve, with a recorded-move receipt."},{"name":"scope-tasks","file":"src/components/scope-detail.tsx","renders":"The scope's task list with live counts, stating that closing tasks does not move the hill."},{"name":"scope-history","file":"src/components/scope-detail.tsx","renders":"Dated hill moves including the backwards one, with the note and the person behind each."},{"name":"cycle-settings","file":"src/components/settings-console.tsx","renders":"Cycle shape form with submit validation, a focused error summary linking each field, accepted, read-only and disabled treatments, notification switches and a durable saved receipt. Mounted beside the roster in a tab set on /settings."},{"name":"team-settings","file":"src/components/settings-console.tsx","renders":"Roster table with access selects, seat arithmetic, and person removal behind a typed confirmation."},{"name":"overlay-gallery","file":"src/components/overlay-gallery.tsx","renders":"The overlay layer shown open: dialog, typed confirmation, popover, dropdown menu, tooltip, command palette, date picker and calendar, with their keyboard contract stated."},{"name":"ui/dialog","file":"src/components/ui/dialog.tsx","renders":"Shared primitives: Dialog and ConfirmDialog over one modal layer — scrim, measure box and card — the latter holding its destructive action until the object's name is typed."},{"name":"ui/popover","file":"src/components/ui/popover.tsx","renders":"Shared primitive: a non-modal popover anchored to its trigger, clamped and flipped against the viewport, closing on Escape, an outside press and Tab leaving it."},{"name":"ui/tooltip","file":"src/components/ui/tooltip.tsx","renders":"Shared primitive: a tooltip that opens on hover and focus, dismisses on Escape, and is wired through aria-describedby."},{"name":"ui/command-palette","file":"src/components/ui/command-palette.tsx","renders":"Shared primitives: CommandPalette and the CommandBar trigger — a modal listbox on ui/dialog's layer, driven from one input through aria-activedescendant and opened with Command or Control K."},{"name":"ui/calendar","file":"src/components/ui/calendar.tsx","renders":"Shared primitives: Calendar and DatePicker — a typeable date field beside the full date-grid keyboard model, with non-working days marked."},{"name":"ui/tabs","file":"src/components/ui/tabs.tsx","renders":"Tab set: role=tablist with roving tabindex, Arrow Left and Right, Home and End, automatic activation, and a focusable panel wired both ways. Mounted on /settings."},{"name":"ui/disclosure","file":"src/components/ui/disclosure.tsx","renders":"Disclosure: a summary button carrying aria-expanded and aria-controls over a labelled region that Escape collapses from inside, returning focus to the summary."},{"name":"ui/progress","file":"src/components/ui/progress.tsx","renders":"Determinate progress bar with role=progressbar and aria-valuetext, used for elapsed time and spent appetite and never for a scope, which is what the hill is for."},{"name":"ui/skeleton","file":"src/components/ui/skeleton.tsx","renders":"The loading treatment: flat non-animated blocks hidden from assistive technology, paired with a status line that says what is arriving."},{"name":"ui/toast","file":"src/components/ui/toast.tsx","renders":"Transient message region: role=status or role=alert, dismissible, Escape clears the newest, and a message carrying an action never expires on a timer."},{"name":"ui/fields","file":"src/components/ui/fields.tsx","renders":"Shared form primitives: Field, Input, Textarea, Select, Checkbox, Toggle, FieldSet and ErrorSummary, carrying label, description, invalid, read-only, disabled and submitting treatments over a dedicated --field-border."},{"name":"ui/button","file":"src/components/ui/button.tsx","renders":"Shared primitives: Button, ButtonLink and IconButton across primary, secondary, ghost and danger, with disabled and pending states from one buttonClasses helper."},{"name":"ui/badge","file":"src/components/ui/badge.tsx","renders":"Shared primitives: Badge, StateBadge, ShippedBadge, AppetiteChip, PriorityMark and Eyebrow — lifecycle by ring shape, priority by bar shape, never by colour alone."},{"name":"ui/panel","file":"src/components/ui/panel.tsx","renders":"Shared primitives: Panel with its sunken, card and raised surfaces, PanelHeader, PanelBody, PanelFooter and the designed EmptyState."},{"name":"ui/table","file":"src/components/ui/table.tsx","renders":"Shared primitives: TableFrame with its own scroll container and caption, Th, Td and the removable FilterChip; the sortable head is ui/sort-head."},{"name":"ui/hill","file":"src/components/ui/hill.tsx","renders":"Shared primitives: HillTrack, HillRidge and OwnerMark — the signature curve computed from the scope fixture at three sizes, with text alternatives from the same data."},{"name":"ui/metric","file":"src/components/ui/metric.tsx","renders":"Shared primitives: HeroFigure for the one oversized number a view leads with, Metric for supporting figures, and AppetiteWell for a fixed track with time drawn inside it."},{"name":"ui/dashboard-layout","file":"src/components/ui/dashboard-layout.tsx","renders":"Shared primitive: the shell boundary every route mounts inside, owning the rail, the sticky header, scroll ownership and the theme's one extra slot, cycleBar."},{"name":"ui/mode-toggle","file":"src/components/ui/mode-toggle.tsx","renders":"Shared primitive: the ModeToggle that switches light and dark on the theme scope and persists the choice."},{"name":"ui/brand","file":"src/components/ui/brand.tsx","renders":"Shared primitive: AppetiteMark, the hill reduced to a mark, and the one bespoke drawing this theme allows."},{"name":"ui/dropdown-menu","file":"src/components/ui/dropdown-menu.tsx","renders":"Shared primitive: DropdownMenu with arrow keys, Home, End, 700ms type-ahead, roving tabindex, a check glyph on a menuitemcheckbox row, and a disabled item that keeps its focus so the sentence explaining it can be read."},{"name":"ui/sort-head","file":"src/components/ui/sort-head.tsx","renders":"Shared primitive: a sortable column head whose whole cell is the control, carrying aria-sort only where a comparator exists and a written sentence saying what activating it does next."},{"name":"ui/hint","file":"src/components/ui/hint.tsx","renders":"Shared primitive: a one-clause helper line with a named trigger beside it that opens the reasoning in the popover card, so a panel keeps one line on the page and the sentences one press away."}],"accent_mode":"second-hue","tokens":{"--background":"#f3f4f7","--foreground":"#222326","--card":"#ffffff","--card-foreground":"#222326","--surface-sunken":"#e9ebf1","--surface-raised":"#ffffff","--surface-overlay":"#ffffff","--overlay-border":"#868992","--muted":"#ebecf0","--muted-foreground":"#60636c","--secondary":"#ebecf0","--secondary-foreground":"#222326","--secondary-hover":"#dfe1e7","--secondary-active":"#d3d6de","--border":"#d0d3dc","--border-strong":"#7f838d","--ring":"#008585","--shadow-color":"#181a20","--scrim":"#101116","--nav":"#ebecf0","--nav-foreground":"#222326","--nav-muted":"#60636c","--nav-hover":"#e1e2e6","--nav-selected":"#ffffff","--nav-selected-foreground":"#005555","--nav-border":"#d0d3dc","--primary":"#058f8f","--primary-foreground":"#001111","--primary-hover":"#1e9a9a","--primary-active":"#2ba0a0","--primary-soft":"#d8f3f2","--primary-border":"#2b8d8d","--primary-ink":"#005d5d","--accent":"#7137b6","--accent-foreground":"#f9f7fe","--accent-hover":"#5f279e","--accent-active":"#4c1b82","--accent-soft":"#f0e9fe","--accent-border":"#8d62cd","--field":"#ffffff","--field-border":"#d0d3dc","--control":"#f6f7f8","--control-border":"#d0d3dc","--control-hover":"#f0f1f4","--control-active":"#e5e6eb","--selection":"#d9eceb","--selection-foreground":"#222326","--selection-border":"#47807f","--link":"#005d5d","--link-hover":"#003f3f","--link-visited":"#58437a","--info":"#0056bc","--info-foreground":"#f7faff","--info-soft":"#e1eeff","--info-border":"#3d7ece","--info-fill":"#2463eb","--positive":"#0b6c3f","--positive-foreground":"#f5fcf7","--positive-soft":"#ddf6e5","--positive-border":"#498e65","--positive-fill":"#019a5a","--positive-hover":"#237b52","--positive-active":"#2d815a","--warning":"#804f00","--warning-foreground":"#fff9f2","--warning-soft":"#ffebd1","--warning-border":"#a86f21","--warning-fill":"#bd7606","--critical":"#a2191b","--critical-foreground":"#fff8f7","--critical-soft":"#ffe7e4","--critical-border":"#c7534a","--critical-fill":"#c92626","--critical-hover":"#ab3032","--critical-active":"#b03c3d","--priority-none":"#6b7280","--priority-low":"#3c83f6","--priority-medium":"#aa8004","--priority-high":"#db6301","--radius-sm":"0.25rem","--radius":"0.375rem","--radius-lg":"0.5rem","--radius-xl":"0.75rem","--display-weight":"600","--text-caption":"0.75rem","--leading-caption":"1.4","--text-small":"0.875rem","--leading-small":"1.45","--text-body":"1rem","--leading-body":"1.55","--text-lead":"1.125rem","--leading-lead":"1.6","--text-d1":"1.5rem","--leading-d1":"1.25","--text-d2":"1.875rem","--leading-d2":"1.2","--text-d3":"clamp(2.125rem, 1.98rem + 0.62vw, 2.344rem)","--leading-d3":"1.14","--text-d4":"clamp(2.5rem, 2.2rem + 1.3vw, 2.93rem)","--leading-d4":"1.06","--tracking-d1":"-0.012em","--tracking-d2":"-0.018em","--tracking-d3":"-0.022em","--tracking-d4":"-0.028em","--tracking-label":"0.08em","--font-display":"var(--font-appetite-display), ui-sans-serif, system-ui, sans-serif","--font-text":"var(--font-appetite-text), ui-sans-serif, system-ui, sans-serif","--font-sans":"var(--font-text)","--font-mono":"var(--font-appetite-mono), ui-monospace, monospace"},"tokensDark":{"--background":"#222326","--foreground":"#fafafa","--card":"#292a2e","--card-foreground":"#fafafa","--surface-sunken":"#1b1c1f","--surface-raised":"#303136","--surface-overlay":"#35363b","--overlay-border":"#767980","--muted":"#303136","--muted-foreground":"#9fa1a8","--secondary":"#303136","--secondary-foreground":"#fafafa","--secondary-hover":"#35363b","--secondary-active":"#3a3b41","--border":"#3a3b41","--border-strong":"#83868e","--ring":"#65d2d1","--shadow-color":"#000000","--scrim":"#050608","--nav":"#18191b","--nav-foreground":"#fafafa","--nav-muted":"#9fa1a8","--nav-hover":"#232326","--nav-selected":"#303136","--nav-selected-foreground":"#65d2d1","--nav-border":"#3a3b41","--primary":"#058f8f","--primary-foreground":"#001111","--primary-hover":"#1e9a9a","--primary-active":"#2ba0a0","--primary-soft":"#103939","--primary-border":"#428e8e","--primary-ink":"#65d2d1","--accent":"#be9df7","--accent-foreground":"#191127","--accent-hover":"#cfb4ff","--accent-active":"#decaff","--accent-soft":"#382950","--accent-border":"#8a70b8","--field":"#1c1d20","--field-border":"#3a3b41","--control":"#303136","--control-border":"#43454b","--control-hover":"#35363b","--control-active":"#3a3b41","--selection":"#243c3b","--selection-foreground":"#fafafa","--selection-border":"#5d9190","--link":"#65d2d1","--link-hover":"#97e9e8","--link-visited":"#c4acf2","--info":"#7cb4fc","--info-foreground":"#07162a","--info-soft":"#173355","--info-border":"#5682bb","--info-fill":"#447aee","--positive":"#6cd397","--positive-foreground":"#021c0d","--positive-soft":"#183b26","--positive-border":"#509069","--positive-fill":"#2dc87a","--positive-hover":"#65c28d","--positive-active":"#62ba87","--warning":"#f2ae59","--warning-foreground":"#231200","--warning-soft":"#432e14","--warning-border":"#a37640","--warning-fill":"#f6a523","--critical":"#f6857a","--critical-foreground":"#270d0a","--critical-soft":"#50221e","--critical-border":"#bb635a","--critical-fill":"#da3e3e","--critical-hover":"#e27c72","--critical-active":"#d7776f","--priority-none":"#7a8190","--priority-low":"#5593f7","--priority-medium":"#f6be13","--priority-high":"#fa832e","--radius-sm":"0.25rem","--radius":"0.375rem","--radius-lg":"0.5rem","--radius-xl":"0.75rem","--display-weight":"550","--text-caption":"0.75rem","--leading-caption":"1.4","--text-small":"0.875rem","--leading-small":"1.45","--text-body":"1rem","--leading-body":"1.55","--text-lead":"1.125rem","--leading-lead":"1.6","--text-d1":"1.5rem","--leading-d1":"1.25","--text-d2":"1.875rem","--leading-d2":"1.2","--text-d3":"clamp(2.125rem, 1.98rem + 0.62vw, 2.344rem)","--leading-d3":"1.14","--text-d4":"clamp(2.5rem, 2.2rem + 1.3vw, 2.93rem)","--leading-d4":"1.06","--tracking-d1":"-0.012em","--tracking-d2":"-0.018em","--tracking-d3":"-0.022em","--tracking-d4":"-0.028em","--tracking-label":"0.08em","--font-display":"var(--font-appetite-display), ui-sans-serif, system-ui, sans-serif","--font-text":"var(--font-appetite-text), ui-sans-serif, system-ui, sans-serif","--font-sans":"var(--font-text)","--font-mono":"var(--font-appetite-mono), ui-monospace, monospace"},"compatibility":{"next":">=15 <17","react":">=19 <20","tailwind":">=4 <5"},"dependencies":["lucide-react","recharts"],"editions":{"react":{"source":"src","runtime":"react"},"html":{"source":"html/src","output":"html/dist","engine":"nunjucks","runtime":"vanilla","buildRequired":false}},"ui_library":{"version":"1.5.2","files":["html/src/js/_tt/anchor.js","html/src/js/_tt/dialog.js","html/src/js/_tt/dropdown-menu.js","html/src/js/_tt/focusables.js","html/src/js/_tt/layer.js","html/src/js/_tt/mode.js","html/src/js/_tt/popover.js","html/src/js/_tt/presence.js","html/src/js/_tt/reduced-motion.js","html/src/js/_tt/roving.js","html/src/js/_tt/sort-head.js","html/src/js/_tt/table-sort.js","html/src/js/_tt/tabs.js","html/src/js/_tt/toast-queue.js","html/src/js/_tt/toast.js","html/src/js/_tt/tooltip.js","src/components/ui/_tt/anchor.ts","src/components/ui/_tt/dialog.axes.ts","src/components/ui/_tt/dialog.fixtures.ts","src/components/ui/_tt/dialog.tsx","src/components/ui/_tt/dropdown-menu.axes.ts","src/components/ui/_tt/dropdown-menu.fixtures.ts","src/components/ui/_tt/dropdown-menu.tsx","src/components/ui/_tt/focusables.ts","src/components/ui/_tt/hint.axes.ts","src/components/ui/_tt/hint.fixtures.ts","src/components/ui/_tt/hint.tsx","src/components/ui/_tt/host.ts","src/components/ui/_tt/ids.ts","src/components/ui/_tt/layer.ts","src/components/ui/_tt/layout-effect.ts","src/components/ui/_tt/popover.axes.ts","src/components/ui/_tt/popover.fixtures.ts","src/components/ui/_tt/popover.tsx","src/components/ui/_tt/presence.ts","src/components/ui/_tt/recipe.ts","src/components/ui/_tt/reduced-motion.ts","src/components/ui/_tt/roving.ts","src/components/ui/_tt/skeleton.axes.ts","src/components/ui/_tt/skeleton.fixtures.ts","src/components/ui/_tt/skeleton.tsx","src/components/ui/_tt/sort-head.axes.ts","src/components/ui/_tt/sort-head.fixtures.ts","src/components/ui/_tt/sort-head.tsx","src/components/ui/_tt/table-sort.ts","src/components/ui/_tt/tabs.axes.ts","src/components/ui/_tt/tabs.fixtures.ts","src/components/ui/_tt/tabs.tsx","src/components/ui/_tt/toast-queue.ts","src/components/ui/_tt/toast.axes.ts","src/components/ui/_tt/toast.fixtures.ts","src/components/ui/_tt/toast.tsx","src/components/ui/_tt/tooltip.axes.ts","src/components/ui/_tt/tooltip.fixtures.ts","src/components/ui/_tt/tooltip.tsx"]},"license":"standard","design":{"typefaces":{"display":"Familjen Grotesk","text":"Geist","mono":"Geist Mono"},"typeRatio":1.25,"signature":"The hill track: one curve, y = h·sin²(πx), at three sizes — a 960px full-bleed ridge on /cycle, a 112px inline track in every row that names a scope, and the same curve as a 640px pointer-and-arrow-key slider on /scope. Every marker's x is a scope's fixture hill value, so the drawing cannot disagree with the table beside it.","surface_kind":"console","built_against":"2026-08-23","levers":{"theme":"light","density":"compact","accent_mode":"single-saturated","motion":"restrained","borders":"hairline","radius":"small","texture":"none","shadow":"none"},"icons":{"set":"lucide","stroke":2,"ladder":["size-3","size-3.5","size-4"],"contexts":{"nav":"glyph","panel_header":"mark","fact_row":"none","feature_grid":"none"}},"disclosure":{"helper_line":"one-clause","sentence":"hint","paragraph":"details","parallel_views":"tabs","secondary_object":"drawer"},"colour_budget":{"brand_max_pct":10,"concentration_pct":70,"regions":[]}}}