Theming guide

A theme is a plain CSS file. Anything goes: you can restyle every element the app renders. This page describes the theming API, version 1: the parts the app promises to keep stable. The built-in themes use only this API. A theme that relies on anything else (other class names, element structure) works today but may break with any release.

Start from the starter template, then load it as your own theme (Theme → Use your own CSS file) or, as host, as the room's theme (Settings → Room theme → Upload CSS).

How themes are loaded

The app's styles live in cascade layers, declared in this order: @layer locked, base, theme.room, theme.personal; A room theme is imported into theme.room, a personal theme into theme.personal, so a personal theme overrides the room's, and both override the app's base styles without needing higher specificity. The whole file goes into its layer, including any @layer blocks inside it.

A few rules are locked and cannot be overridden, not even with !important: the “Use default theme” button (.itd-theme-escape), so everyone can always get back to the default look, and the display, visibility and opacity of html, body and itd-app, which hold it.

A room theme and a personal theme are active at the same time, and @keyframes names are global: when both define the same name, one silently replaces the other. Prefix your keyframes with your theme's name, for example @keyframes my-theme-blink. The itd- prefix is the app's own: its keyframes are named itd-…, so a theme must not use it.

Design tokens

Set these custom properties on :root. The base styles use them everywhere.

Property Used for
color-scheme Not a token, but set it: light, dark or light dark (default, follows the system). Default colors use light-dark().
--itd-font Font family of everything
--itd-bg Page background
--itd-surface Inputs, buttons, menus, dialogs, bottom sheets. Keep it opaque.
--itd-panel The columns and the stage (default: --itd-surface). May be translucent, to let the page background show through.
--itd-backdrop The dimmed page behind dialogs and bottom sheets
--itd-fg Text
--itd-muted Secondary text, badges
--itd-border Borders and separators
--itd-accent, --itd-accent-fg Primary buttons, active item, chart bars; text on accent
--itd-warning Warnings, an expired timer, the highest vote
--itd-accent-soft Tinted backgrounds: the active item, the final-estimate bar, chips
--itd-success, --itd-success-soft Estimated items, voted, consensus
--itd-caution, --itd-caution-soft Incomplete items, warnings, a wide spread
--itd-danger, --itd-danger-fg Errors and destructive actions (Delete); text on a danger background
--itd-outline Borders of fields (at least 3:1 against the surface)
--itd-shadow, --itd-shadow-raised Panels; the stage, menus, dialogs and the picked card
--itd-card-bg, --itd-card-fg, --itd-card-border Voting cards
--itd-card-selected-bg, --itd-card-selected-fg The card you picked
--itd-radius Corner radius
--itd-radius-pill Fully rounded shapes: chips, badges, the timer, progress bars (default: 999px; 0 squares them)
--itd-focus The keyboard focus ring (default: --itd-accent)
--itd-gap Spacing between and inside panels

Stable class names

Every class that is part of the API starts with itd-. Classes without the prefix are the app's own and may change with any release; a theme should not rely on them.

Area Classes
Start page .itd-start .itd-start-header .itd-start-tagline .itd-start-cards .itd-start-create .itd-start-about .itd-start-steps .itd-start-features .itd-start-faq (what the app is, below the tools) .itd-start-pick .itd-input-row .itd-start-snapshot .itd-kept-rooms .itd-kept-room .itd-kept-room-name .itd-kept-room-meta
Room frame .itd-room .itd-room-header .itd-room-title .itd-room-name .itd-room-actions .itd-room-layout .itd-notices .itd-warning .itd-error .itd-succession-note .itd-stranded (others are in the room but none can be reached)
Header actions .itd-download-snapshot .itd-load-snapshot .itd-copy-link .itd-open-settings .itd-profile-button .itd-profile-name #profile (the profile menu) .itd-leave-room (its way back to the start page) .itd-menu
Name dialog .itd-name-dialog .itd-name-prompt .itd-name-prompt-title .itd-name-prompt-hint
Items .itd-items .itd-items-header .itd-item-list .itd-item .itd-item-drag-handle .itd-item-select .itd-item-main .itd-item-text .itd-item-status .itd-item-meta .itd-item-estimate .itd-item-actions .itd-item-menu-button .itd-item-menu .itd-items-progress .itd-items-total .itd-add-items-panel .itd-add-items
Stage .itd-round .itd-round-header .itd-round-phase .itd-round-item .itd-round-hint .itd-round-empty .itd-action-band (the host's bar at the bottom: start voting, reveal now, agreed estimate; it sticks to the bottom while the stage scrolls under it, so give it an opaque background)
Voting .itd-start-voting .itd-timer-field .itd-timer-choices .itd-timer-preset .itd-round-status .itd-progress .itd-progress-text .itd-progress-line .itd-voter-avatars .itd-voter-avatar .itd-timer .itd-countdown .itd-countdown-number .itd-countdown-label .itd-cards .itd-card .itd-voting-actions .itd-text-answer .itd-withdraw .itd-reveal-now
Reveal .itd-results .itd-verdict .itd-verdict-text .itd-verdict-title .itd-votes .itd-vote .itd-vote-value .itd-vote-voter .itd-vote-extreme .itd-stats .itd-stat .itd-stat-label .itd-stat-value .itd-last-round .itd-disclosure-title (the summary of a foldable section)
Agreed estimate .itd-final-estimate-band .itd-final-estimate .itd-estimate-text .itd-estimate-label .itd-estimate-hint .itd-estimate-links .itd-estimate-input .itd-estimate-saved-value .itd-estimate-value .itd-save-estimate .itd-use-suggestion .itd-vote-again .itd-revote-confirm .itd-discard-votes .itd-keep-votes · for voters: .itd-final-estimate-value
Participants .itd-participants .itd-participant .itd-participant-info .itd-participant-name .itd-participant-badges .itd-badge .itd-you-marker .itd-vote-status-cell .itd-vote-status .itd-avatar .itd-observers-title .itd-observers (the observers, listed after the voters)
Bottom sheets (narrow screens) .itd-sheet .itd-items-sheet .itd-people-sheet .itd-sheet-handle .itd-sheet-buttons .itd-sheet-button .itd-items-button .itd-people-button .itd-sheet-badge
Dialogs .itd-settings .itd-settings-form .itd-settings-section .itd-deck-preview .itd-deck-cards .itd-deck-card .itd-room-theme .itd-transfer-host .itd-theme-dialog .itd-theme-tiles .itd-theme-tile .itd-theme-swatch .itd-theme-swatch-card .itd-theme-swatch-accent .itd-theme-tile-name .itd-dialog-body .itd-dialog-actions .itd-avatar-colors (the profile menu's color pickers) .itd-avatar-seed-toggle .itd-avatar-seed (its avatar, which shows the seed field)
Buttons and bits .itd-primary .itd-tonal .itd-chip .itd-link-button .itd-icon-button .itd-icon .itd-count .itd-hint .itd-visually-hidden .itd-button (a label that looks like a button: the file pickers) .itd-dismiss-error

Besides the classes, these elements are part of the API: the page (html, body), links (a), the form controls (button, input, select, textarea) and the modal dialogs (dialog). Panels are section elements; headings are h1–h3. Anything else inside a component is reached through its class. Below 1024px wide the layout collapses: the item list and the participants become bottom sheets (.itd-sheet) opened by .itd-sheet-buttons.

State in data attributes

Element Attribute and values
.itd-item data-status: open, estimated, incomplete, rejected · data-active: true, false · data-dragging while dragged (and on .itd-item-list) · custom property --i: its position in the whole list
.itd-sheet data-dragging while its handle is dragged (the sheet follows the finger)
.itd-item-select aria-current=true on the active item
.itd-round data-phase: none, discussing, voting, revealed · data-counting-down: true, false
.itd-round-phase data-phase: discussing, voting, revealing, revealed
.itd-card data-selected: true, false · data-value: its label · custom property --i: its position in the deck
.itd-timer data-ending (last 15 s), data-expired: true, false
.itd-timer-preset aria-pressed: true for the chosen preset
.itd-voter-avatar data-voted: true, false
.itd-progress-line custom property --done: 0–1
.itd-results data-animate: true for the round just revealed, false for an earlier round shown again (animate only the first)
.itd-final-estimate-value data-status: the item's status; incomplete and rejected keep an estimate that no longer counts
.itd-verdict data-verdict: consensus (above the votes, which then lie in one pile), wide (under the statistics)
.itd-vote data-kind: card, text · data-number: on a text answer that is a number · data-extreme: min, max, none · custom property --i: its index, for staggered animation · custom property --rank: 0 (lowest) to 1 (highest) by value (in a deck without values, by the card's place in the deck), 0.5 when all are equal; unset for votes that don't count
.itd-vote-value data-value: the vote as shown
.itd-countdown data-count: the seconds left (3, 2, 1), changing every second
.itd-stat data-stat: min, max, mean, median · custom property --i: 0–3 in that order
.itd-final-estimate-band data-state: editing, saved
.itd-participant data-connected: true, false · data-reachability: self, connecting, connected, unreachable · data-role: voter, observer · data-vote: none, voted, pending · custom property --i: its position in the list
.itd-badge data-badge=host on the host badge
.itd-copy-link data-copied for two seconds after copying
.itd-sheet :popover-open while open as a bottom sheet · custom property --sheet-open: 1→0 while dragged down (used by its backdrop)
.itd-theme-tile data-theme-id: the theme's ID, or room
html data-room-theme, data-personal-theme: the active theme's ID, or custom

Versioning

Version 1. Removing or renaming anything listed here makes a new version, announced in the app’s release notes. Additions don’t.