# Lozenge Lozenge is a Jira-flavoured design system: SCSS + CSS custom properties, plain class-based HTML components (no JavaScript required for core components — tabs, accordions, modals and dropdowns ride on radios,
, and popover/anchor positioning). Theming is runtime-parametric via four CSS custom properties on :root: - --lz-contrast contrast boost, 0..1 (shifts lightness/chroma of system colors) - --lz-accent-hue OKLCH hue of the accent ramp (default 260.48 — Jira blue) - --lz-accent-chroma accent chroma multiplier (0 = grayscale accent) - --lz-glass frosted-glass materials on/off (navbar, modal, dropdown, flag) Dark scheme: set data-theme="dark" on (system tokens carry light and dark values; components never hard-code scheme colors). Color layers: --lz-ref-* are raw ramps (accent ramp is parametric); --lz-sys-* are the semantic tokens components consume. Always style with --lz-sys-*. Markup contracts below are machine-checked by scripts/lozenge-lint.mjs against specs/*.json. Rules of thumb: variant classes on one axis are mutually exclusive; "required" axes must have exactly one class; glass surfaces (navbar, dropdown-menu, modal, flag) must never nest inside each other. ## Components ### accordion Expansion-panel list (Material ExpansionPanel equivalent) on native
/: a bordered surface group of panels separated by separator borders, each with a rotating-chevron header and a padded body. The platform supplies expand/collapse, keyboard, and semantics. Give every panel the same name="…" attribute for exclusive-open behavior (opening one closes the others) with zero JS; omit name to allow several panels open at once. - Root: `.accordion` (element: div | section) - Required structure: `details.accordion-panel`, `details.accordion-panel > summary.accordion-header`, `.accordion-body` ```html
What is Lozenge?

A zero-JS HTML+CSS design system.

Does it need a runtime?

No — platform primitives only.

``` ### avatar A circular user/entity avatar showing an image or initials fallback, with optional presence dot and square variant for projects/apps. A size class is mandatory — the base class sets no dimensions. Use .avatar-group to overlap several avatars (watchers, assignees). - Root: `.avatar` (element: span) - Variants (size; required, pick one): `avatar-xs`, `avatar-sm`, `avatar-md`, `avatar-lg`, `avatar-xl` - Variants (presence; optional, pick one): `avatar-online`, `avatar-busy`, `avatar-offline` - Modifiers: `avatar-square` ```html Dana K ``` ### avatar-group Overlapping horizontal stack of avatars, each gaining a surface-colored ring; consecutive avatars overlap by 8px. - Root: `.avatar-group` (element: span | div) - Required structure: `.avatar` ```html AB+4 ``` ### badge A small pill for numeric counters — unread counts, story points, +/− deltas. Use it next to a label or inside .issue-card-meta / .board-column-header. For status words use .lozenge instead; for free-text labels use .tag. - Root: `.badge` (element: span) - Variants (tone; optional, pick one): `badge-primary`, `badge-important`, `badge-added`, `badge-removed` ```html 25 ``` ### board Kanban board layout: a horizontally scrolling flex row of fixed-width columns, each with a header and a cards well. Use it as the main work view of a project; fill .board-column-cards with .issue-card elements. - Root: `.board` (element: div) - Required structure: `.board-column` ```html
To do 4
Fix login loop
``` ### bottom-nav Bottom navigation bar for phone-width layouts (Material NavigationBar equivalent): a 56px frosted-glass bar of 3–5 evenly-spread top-level destinations, each an icon over an 11px label with a pill indicator behind the active item's icon. Use exactly one per page, only for top-level destinations — fewer than 3 wants tabs, more than 5 wants a sidebar or rail. The base bar is static; add .bottom-nav-fixed to pin it to the bottom of the viewport. - Root: `.bottom-nav` (element: nav) - Modifiers: `bottom-nav-fixed` - Required structure: `.bottom-nav-item`, `.bottom-nav-item > .bottom-nav-icon` ```html ``` ### button The standard action button. Use ``` ### calendar Month-grid calendar (Material DatePicker-style) that is honestly presentational: it renders whatever days the app gives it — date math, selection state, month navigation, and range logic are app JS or server-rendered markup, not the stylesheet's. The zero-JS behavior path for actually picking a date remains the native (.date-field); use .calendar when you control the state and want the picker look — dashboards, availability displays, server-rendered pickers. Day states: .today (accent ring), .selected (accent fill), .outside (adjacent-month days), .disabled/[disabled]; ranges use .range-start / .in-range / .range-end which fill the whole cell so the band reads as continuous. - Root: `.calendar` (element: div) - Required structure: `.calendar-header`, `.calendar-header .calendar-title`, `.calendar-grid`, `.calendar-grid > .calendar-weekday`, `.calendar-grid > .calendar-day` ```html

August 2026

SuMoTuWeThFrSa
``` ### card Generic raised surface container with optional header/body/footer sections. Use for grouping related content on a page. For draggable-looking Kanban cards use the .issue-card subcomponent instead — it is an independent root, not a .card variant. - Root: `.card` (element: div | section | article) ```html
Sprint 14
Content
``` ### issue-card A single issue on a Kanban board: summary text plus a meta row of issue-type icon, key, badge/lozenge, and avatar. Lives inside .board-column-cards; consecutive issue-cards space themselves 8px apart. - Root: `.issue-card` (element: div) - Required structure: `.issue-card-summary` ```html
Fix login redirect loop
PROJ-423JC
``` ### issue-type 16px colored square standing in for an issue-type glyph icon (story/bug/task/epic). Fixed categorical colors, deliberately not accent-driven. Used in .issue-card-meta, table cells, and inline next to titles. - Root: `.issue-type` (element: span) - Variants (type; required, pick one): `issue-type-story`, `issue-type-bug`, `issue-type-task`, `issue-type-epic` ```html ``` ### carousel Scroll-snap carousel (Material's CarouselView, Lozenge-flavoured): a horizontally scrolling strip of snap-aligned cards. Zero JS — scrolling, snapping, momentum, keyboard (when focusable content is inside) and RTL all come from the platform's scroll machinery. Use .carousel-hero for a center-snapping single-feature strip. - Root: `.carousel` (element: div | ul) - Variants (snap; optional, pick one): `carousel-hero` - Modifiers: `carousel-hero` - Required structure: `.carousel-item` ```html ``` ### chip Compact 24px pill for selection and input, built on native form controls with zero JavaScript — the Lozenge take on Material's chip family. Three recipes share the .chip skin: choice chips (single-select) are a
``` ### comment-editor Reply box: the current user's avatar beside a .form-control textarea. Usually the last row of a .comment-thread; also usable inside .comment-replies for inline replying. - Root: `.comment-editor` (element: div | form) - Required structure: `textarea.form-control` ```html
JC
``` ### date-field Styled native date input: . The native calendar picker IS the behavior layer (keyboard entry, locale formats, AT support) — the class only normalizes the WebKit picker-indicator (opacity, hover wash) and the intrinsic edit-field padding so the control lines up with text inputs. No custom calendar. - Root: `input.date-field` (element: input) - Variants (base-skin; required, pick one): `form-control` - Modifiers: `is-invalid`, `form-control-compact` ```html ``` ### date-range Row layout for a start/end pair: two .date-field inputs flexing equally around an en-dash separator. Constrain the pair with min/max attributes (set min on the end field to the start value in your app) — the layout itself imposes no logic. - Root: `.date-range` (element: div) - Required structure: `.date-field` ```html
``` ### drawer Side panel on a native : full-height glass surface anchored to the inline-end edge (inline-start with .drawer-start), sliding in over a blanket backdrop. Use for issue detail panes, filter panels, and secondary workflows that keep page context visible. Open it declaratively with

LOZ-14 — Login page throws a redirect loop on expired session.

``` ### dropdown Dropdown menu (Material's MenuAnchor/PopupMenuButton, Lozenge-flavoured), zero JS. Preferred recipe: a trigger button with popovertarget opens a [popover].dropdown-menu anchored to it (implicit-anchor positioning) — the platform supplies open/close, light-dismiss, Esc, top-layer, and focus return. Items close the menu on activation via popovertarget + popovertargetaction="hide"; plain links just navigate. Legacy recipe: .dropdown wrapper with a class-toggled .open. Optional /lozenge-sprinkle.js adds arrow-key roving focus and typeahead. - Root: `.dropdown-menu` (element: div) - Variants (placement; optional, pick one): `dropdown-menu-end` - Modifiers: `dropdown-menu-end` - Required structure: `.dropdown-item` ```html ``` ### empty-state Centered explanation for a region with nothing in it yet (empty backlog, no search results, no notifications): optional illustration circle, a title, subtle description text, and a row of next-step actions. Max 460px wide, centered in its container. - Root: `.empty-state` (element: div | section) - Required structure: `.empty-state-title` ```html

The backlog is empty

Create issues to plan your next sprint.

``` ### fab Floating action button — the screen's single primary action, drawn as a raised accent circle. Use at most one per view. .fab-extended turns it into a pill with icon + visible text; .fab-fixed pins it to the bottom-right corner above the page content. Equivalent of Material's FloatingActionButton, styled Lozenge. - Root: `.fab` (element: button) - Variants (size; optional, pick one): `fab-small` - Modifiers: `fab-small`, `fab-extended`, `fab-fixed` ```html ``` ### forms Form field stack: .form-group wraps one field (label + control + help/error text) and provides vertical rhythm. Controls are subcomponents with their own roots: .form-control (inputs/textareas), .form-select, .form-check (checkbox/radio rows), and .toggle switches. - Root: `.form-group` (element: div | fieldset) ```html
Keep it short.
``` ### form-control Text-style input skin for and