Apply thin subtle scrollbars globally via utilities.css instead of
only on .sidebar-content. Uses scrollbar-width/scrollbar-color for
Firefox and ::-webkit-scrollbar for Chrome/Safari. Removes the
now-redundant sidebar-specific scrollbar rules.
Add a shadcn-inspired tasks list recipe (src/recipes/tasks.cljc) that
composes card, table, badge, button, form, and icon components into a
full task management page with toolbar, data table, and pagination.
Add :size :sm prop to the badge component for compact inline labels
used in the tasks table. Small badges have tighter padding, smaller
font, and full pill border-radius.
Wire the tasks page into all three dev targets (hiccup, replicant,
squint) with navigation and routing. Add small badge demos to the
components overview in all targets.
For reactive frameworks (Eucalypt, etc.) where native <dialog> with
.showModal() isn't practical. dialog-overlay provides the fixed backdrop,
dialog-panel provides the content box with padding/gap/animation.
Shared inner styles (dialog-header, dialog-body, dialog-footer) work
with both native <dialog> and overlay-based patterns.
New stateless components ported from org-mode-agenda-cli reference app:
- view-toggle: Grid/Agenda segmented control
- source-toggles: colored pill buttons for event source filtering
- event-detail-dialog: overlay with title, date, time, tags, source
- error-banner: dismissible error bar
- loading-indicator: pulsing dot for async state
Enhanced existing components:
- calendar-event-grid: added :loading?, :header-actions slots
- agenda-day-group/agenda-list: added :day-actions slot per day header
- Event data format extended with :tags and :source fields
CSS: view toggle, loading pulse animation, error banner, source toggles,
detail dialog overlay — all using theme tokens with dark mode support.
Tests: 126 tests, 794 assertions, 0 failures.
Dev demos updated in all 3 targets with full interactive calendar app.
Add src/ui/calendar.md with full documentation for both calendar
namespaces (picker props, event grid, ticker, agenda, event data
format, date utilities, CSS classes).
Add a minimal markdown-to-hiccup renderer (ui.markdown) that handles
headings, fenced code blocks, tables, lists, inline code, and bold.
Styled with ui/markdown.css using theme tokens.
Each dev target renders the docs inline on the Calendar page:
- Hiccup: slurps the .md file at render time
- Replicant: embeds via compile-time macro (ui.macros/inline-file)
- Squint: fetches from /calendar.md served by Vite
Also fixes calendar event grid day cells to be square (aspect-ratio: 1
with overflow: hidden instead of min-height).
Add date picker and event calendar components inspired by shadcn/radix
Calendar and org-mode-agenda-cli.
Components:
- ui.calendar: Month grid date picker with navigation, today/selected
highlighting, outside-month dimming. Pure date math utilities
(days-in-month, day-of-week, calendar-days, etc.)
- ui.calendar-events: Event-aware grid with colored pills, horizontal
day ticker strip with dot indicators, and agenda list view with
grouped events by day
CSS: Token-based styling with dark mode support for event color
variants (accent/danger/success/warning). Responsive breakpoints.
All three targets supported (squint/cljs/clj). Dev pages show calendar
on its own page with interactive demos (date selection, month nav,
event grid, ticker, agenda list).
Tests: 27 new assertions covering date math, class generation,
component structure, event filtering/sorting.
The hiccup dev server now auto-reloads when source files change:
- Background thread polls src/ and dev/hiccup/src/ every 500ms
- On change, reloads dev.hiccup namespace and all transitive deps
- Injects a polling script that hits /dev/changes and reloads the
browser when the version counter bumps
- Server uses #'handler (var ref) so httpkit picks up redefined fns
No more manual server restarts needed for hiccup development.
Button:
- :icon-left, :icon-right props for buttons with leading/trailing icons
- :icon prop for icon-only buttons (square padding via .btn-icon class)
- Icon size scales with button size (sm→sm, md/lg→sm/md)
Alert:
- Auto-assigns variant-specific icons (circle-check, alert-triangle,
alert-circle, info) per variant
- :icon-name prop to override default, false to suppress
- Layout restructured with .alert-icon + .alert-content wrapper
Badge:
- :icon-name prop adds a leading icon before text
- .badge-icon CSS scales icon to match badge font size
Form input:
- :icon-left and :icon-right props on form-input
- Wraps input in .form-input-wrap with absolutely-positioned icon spans
- Padding adjusts automatically via .form-input--icon-left/right
All three dev targets (hiccup, replicant, squint) updated with demos.
Untrack theme-adapter.js and ignore all three files copied by
`bb build-theme` (theme.css, theme-adapter.js, css-live-reload.js).
These are generated artifacts, not source files.
Two-part solution for automatic CSS updates during development:
1. `bb watch-theme` — polls src/ui/*.css and tokens.edn every 500ms,
rebuilds dist/theme.css and copies to dev targets on change.
2. `dev/css-live-reload.js` — browser-side script that polls /theme.css
and hot-swaps the stylesheet without a full page reload (no FOUC).
The watcher runs automatically in the hiccup tmux pane when using
`bb dev-all`. It can also be run standalone with `bb watch-theme`.
The live-reload script is included in all three dev targets (hiccup,
replicant, squint) and copied by `bb build-theme`.
Horizontal and vertical separator (divider) with CSS classes, ARIA
role="none", and data-orientation attribute. Includes dark mode support,
unit tests, and demos in all three dev targets.
Add interactive sliders for font base size, font ratio, and border
radius scale to the theme adapter panel. Font values follow the
geometric scale (base × ratio^power) matching the EDN token system.
Radius values scale the base px values by a 0–2× multiplier.
Includes apply/reset logic, localStorage persistence, EDN export,
and dirty-state detection for the new parameters.
Adds `card-list` and `card-list-item` to the card module for rendering
a list of items inside a single card background with border separators.
The `:divider` prop controls separator style:
- `:full` (default) — borders go edge-to-edge
- `:inset` — borders are inset from edges by `--size-4`h
The `.form-group` CSS reset `border-radius: 0` only for `.form-input`
and `.form-select`, leaving `.btn` elements with their default radius
on all sides. This caused buttons inside input groups to have rounded
left corners instead of flush edges against adjacent inputs.
Adds a "Radius" section with a Scale slider (0–200%) that
proportionally scales all three radius tokens (sm/md/lg).
At 0% corners are sharp, at 100% they match defaults (6/10/16px),
at 200% they're doubled. Persists in localStorage, resets with
Reset, and is included in the Copy EDN output.
Adds a "Font" section with two sliders:
- Base (0.75–1.25 rem) — the root font size
- Ratio (1.05–1.50) — the geometric scale factor
Live-updates all --font-{xs,sm,base,md,lg,xl,2xl,3xl} variables.
Persists in localStorage, resets with Reset, and is included in
the Copy EDN output.
Adds a "Spacing" section with a Base slider (0.10–0.50 rem) that
live-updates all --size-1 through --size-16 CSS variables. The value
persists in localStorage, resets with the Reset button, and is
included in the Copy EDN output.
README gets a full Installation section with per-target setup
(Babashka, shadow-cljs, Squint) plus CSS linking and updating.
AGENTS.md gets a compact summary for agent context.
OKLCH is a perceptually uniform color space — equal lightness values
produce equal perceived brightness across all hues, unlike HSL where
blue at 50% looks much darker than yellow at 50%.
Color scales now output oklch() CSS values directly:
--gray-500: oklch(0.530 0.035 285);
--accent-500: oklch(0.595 0.230 286);
The browser handles gamut mapping natively. Scale definitions in
tokens.edn use [label lightness chroma] tuples where L is 0-1
perceptual lightness, C is chroma (colorfulness), H is hue degrees.
Theme adapter updated: sliders now control OKLCH hue/chroma,
swatches render with oklch() CSS, Copy EDN outputs OKLCH config.
gen.clj includes oklch->srgb and oklch->hex for validation/tools.
Floating panel in bottom-right of all dev targets lets you:
- Switch presets (Purple, Blue, Neutral, Warm, Rose, Emerald)
- Adjust gray hue/saturation and accent hue/saturation with sliders
- Preview color swatches in real-time
- Copy EDN config to paste into tokens.edn
State persists in localStorage. Panel collapses to a small toggle button.
Hiccup handler changed to use #'handler var for hot-reload.
Replace hardcoded hex color tokens with algorithmic color scales generated
from HSL parameters. Each scale is defined by hue, saturation, and
lightness steps in tokens.edn, then converted to hex via jon/color-tools.
Color scales (gray, accent, danger, success, warning) generate 11 stops
each (50–950) into :root. Semantic tokens (bg-0, fg-0, accent, etc.)
reference scale variables with var(--gray-50), var(--accent-500), etc.
Dark theme switches which stop each semantic token points to.
Gray scale uses hue 240 with tapered saturation for a purplish tint
matching the activity-tracker aesthetic. Accent is vivid purple (hue 252).
Border radii bumped to 6/10/16px for a rounder feel.
To shift the entire palette (e.g. warm gray), change hue/saturation in
tokens.edn and run `bb build-theme`.
- Add universal box-sizing: border-box reset to base styles
- Make alert component responsive with container queries (flex-wrap,
full-width title/body at narrow widths)
- Increase form legend margin-bottom from --size-1 to --size-3
Replace the pattern of reading shadow-cljs.edn, mutating it with
pr-str (which destroyed formatting), and writing it back. Instead,
use shadow-cljs's built-in `#shadow/env` reader tag to read the port
from the SHADOW_HTTP_PORT environment variable at startup, with a
default of 3001.
Both `dev-replicant` and `dev-all` tasks now pass the port via env var
instead of file mutation. This eliminates the dirty-file problem where
the rewritten single-line config would show up as an unrelated change.
The squint watcher can produce truncated .mjs files when it detects
file changes mid-save, causing Vite to serve a broken module and the
browser page to go blank with no terminal errors. Document how to
detect this (check .mjs line count) and recover (touch source file +
hard refresh, or restart the tmux pane).
Instead of rendering error messages as `<small class="form-error">`
below inputs, errors now display as a circle-x icon inside the input
area. Hovering the icon shows the error text in a danger-styled tooltip.
- Wrap children in `.form-field-control` when error is present
- Use existing tooltip + icon components for the error indicator
- Add CSS for positioning, padding-right offset, and danger color overrides
- Update tests to match new structure
- Add pre-commit tmux server check instructions to AGENTS.md
<legend> elements have special rendering behavior inside <fieldset>
that doesn't participate properly in flexbox gap. Change
margin-bottom from 0 to var(--size-1) on .form-fieldset--inline
.form-legend to add visible spacing.
Each target reads the theme from ?theme=dark|light on load and applies
it to data-theme before first paint. A MutationObserver syncs theme
changes back to the URL via replaceState, and a click handler appends
?theme= to cross-port navigation links automatically.
The outer iframe shell (dev/index.html) uses postMessage to track theme
changes from iframes and passes the param when switching tabs.
For hiccup, the server also reads ?theme= and sets data-theme on the
<html> element server-side to prevent any flash of wrong theme.
`a.btn` had `color: inherit` which overrode variant-specific colors
(e.g. `--fg-on-accent` on `.btn-primary`) due to higher specificity
(element+class > class). This caused black text on primary link buttons
in light mode. Let variant classes control text color instead.
Add a `[data-no-transitions]` CSS rule that suppresses all transitions.
The theme toggle in all three dev targets (hiccup, replicant, squint)
sets this attribute before switching, then removes it on the next
animation frame, preventing the jarring animated color shift.
Restructures all three dev targets (hiccup, replicant, squint) to use
the sidebar component as the actual app shell. The sidebar navigates
between three pages:
- Components — all UI component demos
- Icons — categorized gallery with all 50+ icons and size variants
- Sidebar — embedded sidebar example with dashboard layout
The sidebar includes a Targets section that links between all three dev
servers, with ports derived dynamically from the current port so they
stay correct when using a custom base port (bb dev-all 5000).
Port scheme: replicant=base+1, squint=base+2, hiccup=base+3.
Adds a general-purpose icon system (ui.icon) with inline SVG rendering:
- 50+ icons from the Lucide icon set (navigation, actions, objects, UI,
status, dev/technical categories)
- Size variants: sm (--size-4), md (--size-5), lg (--size-6), xl (--size-8)
- Pure data approach: icon paths stored as hiccup vectors, rendered into
SVG with stroke="currentColor" so icons inherit text color
- API: (icon/icon {:icon-name :home :size :lg :class "custom"})
Integrates icons into the sidebar component:
- sidebar-menu-item now accepts :icon-name prop
- Renders icon in a .sidebar-menu-item-icon wrapper at :sm size
All three dev targets updated with icon gallery demo and sidebar icons.
Add global :focus-visible outline style and migrate form components
from :focus box-shadow to :focus-visible outline. Refactor accordion
chevron from CSS ::after pseudo-element to explicit span element.
The `bash -c 'cd dir && cmd1 & cmd2'` pattern splits into two
command lists — cmd2 runs in the original cwd, not after the cd.
Fix by repeating the cd after the `&` in the tmux pane command,
and using `exec` in the standalone dev-squint task.
`bb dev-all 4000` starts all servers offset from base port:
- Replicant: base+1 (4001)
- Squint: base+2 (4002)
- Hiccup: base+3 (4003)
Default base port remains 3000.
Individual tasks also accept PORT env var:
PORT=4003 bb dev-hiccup
For replicant, shadow-cljs.edn is rewritten with the target port
before launching since shadow-cljs has no CLI port override.
Shadow-cljs deprecated external paths (`../../src`). Add a root
`deps.edn` exposing `src/` and reference it via `:local/root` from
`dev/replicant/deps.edn` instead.
Add `ensure-npm` task that runs `npm install` in dev/replicant and
dev/squint when their node_modules directories are missing. Wire it
as a dependency for dev-replicant, dev-squint, and dev-all.