# @f-ewald/components A collection of self-contained Lit web components sharing a Tailwind-inspired design token system. Every component is individually importable and ships its own TypeScript types and a checked-in custom-elements.json manifest. Install: `npm install @f-ewald/components` Import patterns: - Whole library: `import "@f-ewald/components";` (registers every component) - Individual component: `import "@f-ewald/components/.js";` (tree-shakes everything else, including d3 for non-chart components) Theming: components use `var(--ui-*, )` custom properties, so they render correctly with zero external CSS. Override any `--ui-*` property on `:root` (or an ancestor) to retheme, or import the optional `@f-ewald/components/tokens.css` stylesheet as a starting point. ## Form-associated text input with a suggestion dropdown. Works as a drop-in replacement for a plain ``: consumers keep reading `new FormData(form).get("address")` and calling `form.reset()` unchanged. Supports two suggestion sources: - **API** (default): fetches from `endpoint`, a Mapbox Geocoding v6-shaped forward-geocode URL, debounced as the user types. - **Local array**: set `suggestions` to a fixed `AddressSuggestion[]` and the component filters it client-side instead of making any network request. Useful for small/fixed address lists, offline use, or tests. Takes priority over `endpoint` whenever it's non-null. Import: `import "@f-ewald/components/address-autocomplete.js";` Properties: `value` (attribute `value`) : string, default ""; `placeholder` (attribute `placeholder`) : string, default ""; `required` (attribute `required`) : boolean, default false; `disabled` (attribute `disabled`) : boolean, default false; `types` (attribute `types`) : string, default "address"; `endpoint` (attribute `endpoint`) : string, default "https://api.mapbox.com/search/geocode/v6/forward"; `accessToken` (attribute `access-token`) : string, default ""; `bbox` (attribute `bbox`) : string, default ""; `proximity` (attribute `proximity`) : string, default ""; `debounce` (attribute `debounce`) : number, default 300; `minLength` (attribute `min-length`) : number, default 3; `suggestions` (JS property only) : AddressSuggestion[] | null, default null; `selectedSuggestion` (JS property only) : AddressSuggestion | null, default — Events: `address-select` CSS custom properties: `--ui-border`, `--ui-focus-ring`, `--ui-font`, `--ui-font-size-sm`, `--ui-line-height-tight`, `--ui-primary`, `--ui-radius-sm`, `--ui-shadow`, `--ui-surface`, `--ui-surface-muted`, `--ui-text`, `--ui-text-muted` Example: ```html ``` ## Fullscreen confetti animation overlay, rendered on a fixed-position canvas. Starts automatically on first render and stops after `duration` ms. Import: `import "@f-ewald/components/animate-confetti.js";` Properties: `duration` (attribute `duration`) : number, default 6000 Events: none CSS custom properties: none Example: ```html ``` ## Generic form-associated text input with a suggestion dropdown, for any "type to filter a list of `{key, value}` options" use case. Works as a drop-in replacement for a plain `` inside a `
`: set `name` on the element itself and consumers keep reading `new FormData(form).get(name)` and calling `form.reset()` unchanged — the submitted value is the picked option's `value`, while `key` is available via the `option-select` event and the `selectedOption` getter for cases that need the underlying id rather than the display text. Supports two suggestion sources: - **API**: set `endpoint` to a URL that, given a `?=` query string, responds with a JSON array of `{key, value}` objects. Requests are debounced as the user types. - **Local array**: set `options` to a fixed `AutocompleteOption[]` and the component filters it client-side instead of making any network request. Useful for small/fixed lists, offline use, or tests. Takes priority over `endpoint` whenever it's non-null. Import: `import "@f-ewald/components/autocomplete-input.js";` Properties: `value` (attribute `value`) : string, default ""; `placeholder` (attribute `placeholder`) : string, default ""; `required` (attribute `required`) : boolean, default false; `disabled` (attribute `disabled`) : boolean, default false; `endpoint` (attribute `endpoint`) : string, default ""; `queryParam` (attribute `query-param`) : string, default "q"; `debounce` (attribute `debounce`) : number, default 300; `minLength` (attribute `min-length`) : number, default 3; `options` (JS property only) : AutocompleteOption[] | null, default null; `selectedOption` (JS property only) : AutocompleteOption | null, default — Events: `option-select` CSS custom properties: `--ui-border`, `--ui-focus-ring`, `--ui-font`, `--ui-font-size-sm`, `--ui-line-height-tight`, `--ui-primary`, `--ui-radius-sm`, `--ui-shadow`, `--ui-surface`, `--ui-surface-muted`, `--ui-text`, `--ui-text-muted` Example: ```html ``` ## Declarative metadata for one calendar event, consumed by a parent `calendar-month` or `calendar-year`. Read-only/non-interactive; renders nothing itself. Import: `import "@f-ewald/components/calendar-entry.js";` Properties: `start` (attribute `start`) : string, default ""; `end` (attribute `end`) : string, default ""; `label` (attribute `label`) : string, default ""; `color` (attribute `color`) : StatusPillColor, default "neutral"; `href` (attribute `href`) : string | undefined, default — Events: none CSS custom properties: none Example: ```html Vacation Out of office Road trip along the California coast with several scenic stops Return July 19 at 6 PM ``` ## One month rendered as a top-to-bottom list of days — weekends and today highlighted, with declarative `calendar-entry` children shown as colored bars spanning the days they cover. An entry's title uses its first visible day; every remaining visible day becomes one shared body for wrapped details and an optional ending footer. Overlapping entries stack into side-by-side lanes rather than being layered/hidden. Read-only. Lanes are computed independently per instance, from only the entries overlapping this month — an entry spanning a month boundary may therefore land in a different lane index in the adjacent month's `calendar-month`. This is an accepted v1 limitation: cross-month lane continuity would require a shared parent (`calendar-year`) to assign lanes globally. Entry attributes and slotted title/detail text are observed, so a standalone month re-renders when consumers update declarative metadata. Import: `import "@f-ewald/components/calendar-month.js";` Properties: `year` (attribute `year`) : number, default —; `month` (attribute `month`) : number, default 1 Events: none CSS custom properties: `--ui-border`, `--ui-danger`, `--ui-focus-ring`, `--ui-font`, `--ui-font-size-lg`, `--ui-font-size-sm`, `--ui-font-size-xs`, `--ui-font-weight-bold`, `--ui-font-weight-medium`, `--ui-font-weight-regular`, `--ui-font-weight-semibold`, `--ui-hover-overlay`, `--ui-info`, `--ui-line-height-tight`, `--ui-primary`, `--ui-radius-sm`, `--ui-success`, `--ui-surface`, `--ui-surface-muted`, `--ui-text`, `--ui-text-muted`, `--ui-warning` Example: ```html Vacation Out of office Road trip along the California coast with several scenic stops Return July 19 at 6 PM Talks and workshops Closing keynote · July 20 ``` ## A full year of `calendar-month` blocks, generated from declarative `calendar-entry` children. Each entry is re-projected into the `calendar-month` blocks it overlaps as a freshly-created `calendar-entry` element — the original elements stay slotted here and are never moved, since a DOM node can only have one parent. Read-only. Import: `import "@f-ewald/components/calendar-year.js";` Properties: `year` (attribute `year`) : number, default — Events: none CSS custom properties: `--ui-font` Example: ```html New York Team workshops Closing dinner Friday Coordinate the release across engineering, design, support, and marketing. Monitor adoption and production health throughout the rollout. Public launch · March 18 at 9 AM ``` ## One conversation entry in a chat-style activity feed. Tool calls and "thinking" traces are variants of this component rather than separate ones — they share the same header, collapse behavior, and body card as a normal message, just dimmed and collapsible with an always-visible summary. Import: `import "@f-ewald/components/chat-message.js";` Properties: `role` (attribute `role`) : ChatMessageRole, default "agent"; `variant` (attribute `variant`) : ChatMessageVariant, default "normal"; `author` (attribute `author`) : string, default ""; `timestamp` (attribute `timestamp`) : string | null, default null; `summary` (attribute `summary`) : string, default ""; `collapsible` (attribute `collapsible`) : boolean, default false; `collapsed` (attribute `collapsed`) : boolean, default false Events: `toggle` CSS custom properties: `--ui-border`, `--ui-focus-ring`, `--ui-font`, `--ui-font-mono`, `--ui-font-size`, `--ui-font-size-sm`, `--ui-font-weight-semibold`, `--ui-line-height-normal`, `--ui-primary`, `--ui-radius`, `--ui-radius-sm`, `--ui-surface`, `--ui-text`, `--ui-text-muted` Example: ```html Write notes.md containing a haiku. directory: . filename: notes.md ``` ## Reusable confirmation dialog: overlay + centered card with a slotted body, an optional error line, and Cancel/Confirm actions. Instant `display:none` → `display:flex` toggle (no transitions). Fires `confirm`/`cancel` (bubbling, composed) instead of owning any deletion logic itself — callers stay in charge of the request. Import: `import "@f-ewald/components/confirm-dialog.js";` Properties: `open` (attribute `open`) : boolean, default false; `confirmLabel` (attribute `confirm-label`) : string, default "Delete"; `cancelLabel` (attribute `cancel-label`) : string, default "Cancel"; `danger` (attribute `danger`) : boolean, default true; `busy` (attribute `busy`) : boolean, default false; `error` (attribute `error`) : string | null, default null Events: `confirm`, `cancel` CSS custom properties: `--ui-border`, `--ui-danger`, `--ui-danger-hover`, `--ui-focus-ring`, `--ui-font`, `--ui-font-size`, `--ui-font-size-sm`, `--ui-font-weight-medium`, `--ui-line-height-normal`, `--ui-line-height-tight`, `--ui-on-accent`, `--ui-overlay`, `--ui-primary`, `--ui-primary-hover`, `--ui-radius`, `--ui-radius-sm`, `--ui-shadow-lg`, `--ui-surface`, `--ui-text`, `--ui-text-muted` Example: ```html Are you sure you want to delete this item? ``` ## Small icon button that copies `value` to the clipboard and shows a toast on success/failure (if a `` element is present), and always dispatches a `copy-success`/`copy-error` CustomEvent so consumers without a toast element can react. Defaults to the current page URL if `value` is unset. Import: `import "@f-ewald/components/copy-link-button.js";` Properties: `value` (attribute `value`) : string, default ""; `label` (attribute `label`) : string, default "Copy link"; `disabled` (attribute `disabled`) : boolean, default false Events: `copy-success`, `copy-error` CSS custom properties: `--ui-focus-ring`, `--ui-radius-sm`, `--ui-surface-muted`, `--ui-text`, `--ui-text-muted` Example: ```html ``` ## A generic, presentational table shell: renders a `` from `columns` and one `` per entry in `rows`, with each cell's content produced by `renderCell` (default: plain property lookup on the row object). Knows nothing about what a "row" means — callers own the data shape entirely. Optional `rowHref` makes whole rows clickable (navigating via `location.hash`), without hijacking clicks on nested interactive elements (links/buttons) a cell's rendered content might contain. Import: `import "@f-ewald/components/data-table.js";` Properties: `columns` (JS property only) : DataTableColumn[], default []; `rows` (JS property only) : unknown[], default []; `rowKey` (JS property only) : (row: unknown, index: number) => string | number, default —; `renderCell` (JS property only) : (row: unknown, key: string) => unknown, default —; `rowHref` (JS property only) : ((row: unknown) => string | null) | null, default null; `rowLabel` (JS property only) : ((row: unknown) => string) | null, default null Events: none CSS custom properties: `--ui-border`, `--ui-focus-ring`, `--ui-font`, `--ui-font-size-sm`, `--ui-font-weight-semibold`, `--ui-surface-muted`, `--ui-text`, `--ui-text-muted` Example: ```html ``` ## Inline distance display. Renders miles/feet or km/m, switching units at sensible thresholds (< 0.25 mi → ft; < 0.5 km → m). Supply exactly one of `miles` or `km`; the other stays null. km support is present but reserved for future use. Import: `import "@f-ewald/components/distance-value.js";` Properties: `miles` (attribute `miles`) : number | null, default null; `km` (attribute `km`) : number | null, default null Events: none CSS custom properties: none Example: ```html ``` ## Renders a KDE distribution curve for a named metric with one or more value markers. The SVG viewBox is kept in sync with the element's pixel width via ResizeObserver so that font sizes and stroke widths are always in real pixels regardless of container width. Pass `fontSize` to control all text size (default 10). Pass a single `{label:'', value}` for a single-value display or multiple `{label:'A'|'B'|...}` entries to compare several values. Import: `import "@f-ewald/components/distribution-chart.js";` Properties: `metric` (attribute `metric`) : string, default ""; `values` (JS property only) : DistributionValue[], default []; `markerColors` (JS property only) : string[], default ["#4f46e5", "#d97706", "#0d9488", "#e11d48"]; `fontSize` (attribute `font-size`) : number, default 10 Events: none CSS custom properties: `--ui-border`, `--ui-danger`, `--ui-font`, `--ui-font-size-sm`, `--ui-font-size-xs`, `--ui-primary`, `--ui-radius-sm`, `--ui-surface-muted`, `--ui-text-muted` Example: ```html ``` ## A primary-styled button with a label and chevron that opens an anchored menu of actions — essentially `form-select` minus "current value" semantics: a menu, not a select. Use for a set of mutually exclusive next-step actions (e.g. a failed task's Retry / Close / Backlog). Import: `import "@f-ewald/components/dropdown-button.js";` Properties: `label` (attribute `label`) : string, default ""; `options` (JS property only) : DropdownOption[], default []; `disabled` (attribute `disabled`) : boolean, default false Events: `select` CSS custom properties: `--ui-border`, `--ui-focus-ring`, `--ui-font`, `--ui-font-size-sm`, `--ui-font-weight-medium`, `--ui-line-height-tight`, `--ui-on-accent`, `--ui-primary`, `--ui-primary-hover`, `--ui-radius-sm`, `--ui-shadow`, `--ui-surface`, `--ui-surface-muted`, `--ui-text` Example: ```html ``` ## Jira/GitHub-style click-to-edit text: a display span that turns into an `` (or auto-growing `