Readme

Update Log

Here you can see the current readme file with all updates, breaking changes, and todos.

@leon8ix/jhana-ui/README.md

# Jhana UI

A beautifully dark-themed Svelte and CSS ui-system that embraces glowing bright colors, round corners and fluid transitions.

## Using Jhana UI

Start with the [consumer and agent guide](docs/README.md). It ships in the package under `@leon8ix/jhana-ui/docs/README.md`, alongside the [layout and field contracts](docs/foundations.md) and [help system](docs/help.md).

Stable 2.x releases are developed on `main` and published to the `latest` package channel. Follow the [migration guide](docs/modernization.md) when upgrading from 1.x or a 2.0 preview, including its compatibility audit and [published-package installation](docs/modernization.md#move-a-trial-to-the-published-package). The documentation site follows `main`; packaged guides describe the installed version.

## ToDo

- Finish documentation site
- Add back ActionButton stroke thickening on focus-visible to stroked SVGIcon
- Allow opening a context menu programmatically with an explicit anchor element, without a DOM event

## Versions

### 2.0.0 (261104)

- Added shared layout and field foundations for settings interfaces
    - Added a proportional spacing scale, stacks, wrapping rows, natural-width field grids and scrolling panels; controls align independently of labels and hints, and related values can share the widest field's required width
    - Added Field with labels, visible hints and supplementary help; Field, NumberInput and ColorPicker accept `labelEnd` snippets for reset buttons and other app-owned label actions
    - Unified label and legend alignment and weight; field-group legends above labeled fields or nested groups receive automatic dividers, while legends above radio/toggle choices remain plain
    - NumberInput starts at `8em`, grows for long values and unit suffixes, and has larger stepper touch targets; wrapped hints follow the control's width without resizing it
    - Native separators follow horizontal or vertical layouts; `--sideSpace` defaults to `--space-3` (`1em`) for consistent page and overlay clearance
    - BREAKING: NumberInput retains wrapper class/style props, but selectors relying on its former wrapping label or direct input child need migration; review the [compatibility audit](docs/modernization.md#check-compatibility-first) for shared CSS and markup changes
- Added boxed toggles, shared radio boxes and native radio-card styling
    - Toggle supports state-dependent side labels while reserving both texts' width; radio cards retain native keyboard behavior and stable dimensions
    - Contained hidden inputs to prevent phantom scrollbars in settings panels and removed empty radio-label spacing for indicator-only choices
    - Hints respect the app's text-selection policy
- Added Help and tooltip attachments with text, images, snippets and components
    - One managed tooltip container follows active dialogs and popovers; keyboard access, click/tap pinning and native touch context-menu gestures support explanations without activating their controls
    - Added instant, normal (300 ms) and long (800 ms) delays, with immediate hover help between triggers until the pointer spends 150 ms away; `openOnSelect` controls radio/checkbox selection help independently and defaults off for long delay
    - Hover-only help closes on leaving its trigger; only explicitly pinned explanations accept pointer interaction and text selection, and selecting text keeps them open
    - ActionButton uses its label for an automatic long-delay tooltip; `title` overrides the explanation instead of creating a native browser tooltip
    - Images and custom content share consistent insets and corner radii; tooltip images are not draggable
    - BREAKING: Raised the Svelte peer minimum to 5.29 for attachment support
- Added ColorPicker and color helpers through `/color`
    - Supports configurable RGB, CMYK and Grayscale modes, optional opacity, read-only inspection, a swatch field and a popup with shaded sliders, numeric inputs and live previews
    - Requires a dictionary for visible and accessible labels; English and German dictionaries are separate optional imports
    - Sliders use whole-number channel steps and opacity percentages; numeric inputs preserve typed decimals and support keyboard editing
    - Theme-aware handles adapt their contrast to the tracks and retain closed rings at rest without fractional-border pinholes; added a hex preview helper
- Added SplitButton and joined native buttons
    - SplitButton pairs a default action with alternate options; arrow clicks and right-clicks open a context menu anchored to the whole group
    - Two native buttons can share `.btn-split` with independent actions, text, icons and disabled states; normal, primary and dark variants share one surface with independent interaction outlines
    - Popup footer buttons accept a menu configuration or popup-aware menu factory, with shared disabled/loading states and guarded form submission; fully disabled groups fade their surface and contents together
- Improved context-menu anchoring, layers, placement and animation
    - Attached groups keep descendants interactive, and nested openers anchor to their control group; direct handlers anchor to their own element instead of a clicked icon or text
    - One CtxMenuRoot follows the trigger's active dialog or popover, fixing inert menus inside modals and unintended closure of parent popovers; closing or removing the owner dismisses its menu
    - Menus keep `--sideSpace` viewport clearance, shrink wide content and own scrolling for long content; tall settings menus shift only enough to fit instead of jumping toward the top
    - Opening and closing animations follow the source's bottom midpoint for menus below, top midpoint for menus above, or pointer position for pointer menus, including after viewport shifts and placement flips
    - Fallback menus and tag mappings respond to replacement props after mounting
- Improved popup keyboard behavior and sizing
    - Plain Enter activates the visible primary footer action, including split buttons and custom controls, while preserving focused controls, form validation, disabled/loading guards and nested-layer isolation
    - Added per-popup and app-wide `submitOnEnter` opt-out, neutral initial focus and focus restoration after nested popups close
    - Content-sized dialogs retain their size within `--sideSpace` viewport limits, borders and rounded corners on narrow screens; long content scrolls while headers and footers remain visible
    - Fixed clipped bottom padding and unnecessary outer scrollbars in popups without footer buttons
- Added shared page progress through `/page-progress`
    - PageProgressRoot and the reactive `pageProgress` value let components and ordinary client-side functions update one app-wide indicator; the standalone PageProgress component remains available
    - Added spring smoothing, a stronger leading glow and a completion pause followed by a gradient wipe instead of a backward reset
    - Clearing progress starts the 1000 ms disappearance delay and a 1700 ms glow fade toward 20%; holding at 100% retains full glow and resumed progress restores brightness
    - Shared layer handling keeps page progress visible above dialogs and popovers alongside notifications
- Fixed notifications appearing behind popups or becoming inert in modal dialogs
    - One NotifyRoot follows the active modal through a manual popover, preserving notification state and timers across nested dialogs
    - Notification buttons do not submit surrounding forms
- Expanded update-checker reminders and update preparation
    - Ask later supports split-button choices for timed reminders or successful app saves/exports; ordinary checks respect pending reminders and the default Later delay remains available
    - Per-call `force` overrides postponements and replaces old reminders when the prompt opens
    - Added asynchronous `beforeUpdate` callbacks for saving state before reload, with loading and retry after failure; dismissing the dialog prevents subsequent navigation
- Refined theme inheritance, contrast and icons
    - Nested `theme-*` scopes inherit unspecified background, text and error colors instead of resetting them; `.theme-root` owns base defaults and each scope recalculates its shades
    - Brightened `--cLight` and primary-card text, softened light-button text with `--cDL05`, and added `--cAD90` for primary-button text
    - Icon inherits `currentColor` by default, including selected-card and button states, while preserving explicit color, hover and active overrides; review existing icon and wrapper color overrides when upgrading
- Fixed icon-button interaction states
    - Disabled ActionButton icons retain their color on hover and press, including when disabled by a fieldset
    - ButtonRevert preserves an explicit disabled state when visible, preventing unintended focus and actions; hidden/disabled controls suppress pointer feedback, while keyboard focus retains color and rotation feedback without a native outline
- Fixed NavIndex ignoring replacement `match` props after mounting
- Expanded packaged consumer/agent guides and live documentation
    - Added migration and compatibility guidance, layout, help, color, icon, shading, popup, notification, progress and update-checker recipes with examples based on consumer apps
    - Unified examples as explanation, live demo and source with `1em` spacing; added shading configuration and component playgrounds with browser regression coverage
    - Added an editable icon-converter constant name and grouped its output options
    - Fixed update-log demo loading and lost clicks before hydration; the documentation sidebar displays the full package version
- Finalized stable 2.0 publishing from `main` on the `latest` channel and consolidated the preview update log
    - Documentation archives use normal GitHub releases and retain the existing independent deployment workflow
    - Package publishing tolerates already-published versions so pushes without a new package version can still complete

### 1.61.0 (261001)

- Added a shared README update-log dialog with version navigation, scroll tracking, nested bullets and inline code
    - Exposed `openUpdateLog(readme)` through `popup/update-log`
    - Added a documentation demo that loads the dialog and Jhana UI's README on demand

### 1.60.0 (260929)

- Fixed spinner stroke clipping by making thickness grow inward from the outer edge
    - BREAKING: Changed `strength` from SVG units to a radius ratio between 0 and 1, with a default of 0.2; the SVG box includes a one-unit inset on each side for antialiasing
    - Added button and inline presets with independent size and strength overrides, and migrated package and documentation usages
    - Added size and strength playground controls, examples for every preset and a [consumer migration guide](docs/spinner-migration.md)

### 1.59.1 (260929)

- Fixed combobox styling regressions from the shared-component migration
    - Restored the `cDL05` popup background to match cards
    - Added `inputAlign="center"` to balance padding around inline actions without shifting the text in default start-aligned inputs
    - Updated the migration guide and demos with the alignment prop, error-themed Clear button, and tabular numbers in async option rows

### Maintenance (260928)

- Separated docs build/release and FTP deployment so connection failures can be retried without rebuilding or deleting releases and tags
    - Deployment retries FTP once after 30 seconds, supports manual deployment by docs release tag, and runs uploads one at a time
    - The release workflow passes the published docs tag directly to deployment and retains the incrementing docs-version suffix
- Replaced archived release actions with GitHub CLI and updated checkout to v7 and FTP deployment to v4.4.0

### 1.59.0 (260928)

- Improved component prop hovers with `PrettifySome`, keeping custom props visible and native attribute types compact
    - Updated `@leon8ix/utils` to `^1.14.0`

### 1.58.1 (260928)

- Prevented toggle and radio tracks from shrinking beside wrapped labels, keeping the controls and toggle knob aligned in narrow layouts

### Maintenance (260927)

- Added repository-specific agent instructions adapted from Infinipose and Cover Designer, including release preparation and cross-repository scope rules

### 1.58.0 (260927)

- Added `FSHandleAssignmentStorage` to the `fs-handles` entry point
    - Persisted named assignments to cached file or directory handles in local storage
    - Restored assigned handles with permission checks and opened a picker when an assignment could not be restored
    - Kept one target per assignment, retained compatibility with Infinipose's existing storage format and added assignment reset
    - Supported custom storage keys and picker titles

### 1.57.0 (260927)

- Implemented a reusable editable `ComboBox` with typed options and Svelte rendering snippets
    - Added asynchronous refresh, search, pagination, opening guards, selection overrides and fallback actions
    - Added keyboard navigation, touch scrolling, loading and error states, and popup sizing that follows available space
    - Added separate basic and async file demos, Cover Designer migration documentation and browser regression tests

### 1.56.0 (260819)

- Fixed update checking for nested SvelteKit routes and unavailable version metadata
    - Added optional SvelteKit base-path support
    - Matched visibility checking to its documented opt-in default and cleaned up its event listener
- Added fixed and callback-based update destinations so apps can preserve state in the update URL

### Maintenance (260812)

- Assigned local Vite preview port `48880`

### 1.55.1 (260530)

- Added type declarations for font CSS subpath exports

### 1.55.0 (260207)

- BREAKING: `Popup` changes
    - Renamed Popup.close() to Popup.resolveCurrent()
        - Clarifies what it actually does
    - Popup.promise will now always resolve
        - Returns instead: { ok: true, value } | { ok: false, reason }
        - Resolves after async action in onresolve or onreject has settled (popup is actually closing)
        - Avoids uncaught logs in console
    - Made Popup.resolve() and Popup.reject() async and wait for action to finish
        - Allows spinner to show processing action
    - Custom buttons combined with form need to use button type=button
- Non-breaking popup changes:
    - Fixed popup onclose to oncancel
    - Made PopupButton.text reactive

### 1.54.0 (260131)

- Updated all dependencies to the latest versions

### 1.53.0 (260131)

- Implemented update checker utility /popup/update-checker

### 1.52.1 (260130)

- Fixed ToggleLink color transition

### 1.52.0 (260130)

- Made Spinner use Icon
    - Added hover and active color passing
    - Fixed hover in ctx-menu
- Replaced previous Icon :focus-within with :has(input:focus-visible)
    - As :focus-within was causing unintended behavior, at least with ctx-menu

### 1.51.0 (260130)

- Fixed Icon states for usage with inputs (e.g. ToggleLock and ToggleLink)
    - Hover color reacted to :focus-visible -> added :focus-within
    - Active color reacted to :active -> added :has(input:active)
- Added accent-color :active state to ToggleLock and ToggleLink
- Removed WebKit tap highlight on touch for all styled elements

### 1.50.0 (260127)

- Added tables styling

### 1.49.1 (260124)

- Fixed light .btn:hover not having outline

### 1.49.0 (260124)

- CSS shading system changes
    - Replaced --glow-offset with --glow-brightness
    - Replaced --glow-accent with --glow-is-accent
    - Using transparent colors vs shadow offset for glow fixes transition glitch
    - Added shading to text/number inputs
- Reverted -0.95 outline-offsets back to -1
    - Seems to cause more issues than it solves

### 1.48.0 (260124)

- BREAKING: ctx-menu changes
    - `CtxMenuItemProps`
        - Renamed: runAction > itemAction
        - Renamed: openSubMenu > itemSubMenu
        - Renamed: ctxIconItem > itemIcon
        - Added: ctxIcon
    - Added CtxMenu.openSub()
    - Made use of RenderingContent

### 1.47.2 (260123)

- Added read-only state to input styling

### 1.47.1 (260122)

- Fixed focus-outline sometimes appearing on open dialog

### 1.47.0 (260122)

- Implemented CSS shading system
    - Added shading to .action-list items (e.g. ctx-menu)
    - Added shading to ProgressBar
- Refined FSHandles styling

### 1.46.0 (260120)

- Added outline to .btn.dark to match inputs and cards

### 1.45.0 (260120)

- BREAKING: `RadioTabs` pass down general props to input element
    - This changes the type of `onchange`
- FSManager and according popups now also read and use the browser-provided handle.name
- Optimized FSManager state handling

### 1.44.0 (260116)

- Allowed for passing PickerOptions for FSHandlePicker
- Restructured and polished FSManager TS generics

### 1.43.1 (260115)

- Added /fs-handles to exports

### 1.43.0 (260115)

- Polished fs-handle-cache
- Polished FSManagerState

### 1.42.0 (260115)

- Further polishing of popups system
    - Allowed for passing components and snippets as buttons
- Globalized type RenderingContent and component RenderContent

### 1.41.0 (260114)

- Introduced .btn.dark to allow styling like .btn.tab
- Unified FSManager and FSPicker

### 1.40.0 (260114)

- Separated state and value for popups

### 1.39.1 (260113)

- Fixed FSHandleManager missing strings in dict
- Globalized FSHandleManager icons

### 1.39.0 (260113)

- Major design imprevements
    - 3D-shaded state on hover and interactions
    - Added active style to Toggle and Radio
- Unified FileSystemAccess API handle handling
    - Integrated fs-handle-cache system
    - Added FSManager and FSPicker pre-built popups

### 1.38.1 (260107)

- Added /popup as export to package.json

### 1.38.0 (260107)

- Implemented first working version of popups
- Improved ButtonClose styling with glow effect and transition
- Replaced default dev port with custom :38880

### 1.37.1 (250809)

- Fixed card:hover to only change border-color and not completely replace border style

### 1.37.0 (250808)

- BREAKING: Renamed `prog`/`progress` prop to `value` for both `Spinner` and `PageProgress`
    - This makes it consistent with ProgressBar and HTML's native progress element
- Added ProgressBar component
- Added aria-atts for Spinner and PageProgress

### 1.36.0 (250808)

- BREAKING: Renamed `ProgressBar` to `PageProgress`
- Fixed ProgressBar to use fixed instead of absolute positioning
- Fixed scroll-bar on CodeBlock from being next to header

### 1.35.0 (250802)

- Implemented ctx-menu flipping
    - For anchored to element mode
    - And for global mode: Implemented additional adapter, so margin is not flipped
- Internal: Separated out CtxMenuContent (visually styled content) from CtxMenuRoot (positioning and anchoring logic)

### 1.34.0 (250802)

- Added iconFile

### 1.33.0 (250801)

- Added keyboard shortcut indicator to ctx-menu item ('kbd')
- Improved kbd styling

### 1.32.0 (250801)

- Implemented loading state for async ctx-menu actions

### 1.31.0 (250801)

- Properly implemented font setups
    - Fixed font variable fallbacks and optimized their setup
    - Everthing was defined via font-variation-settings and font-feature-settings
    - This was unnecessary, as all of these were available via standard CSS properties
    - Opsz can be auto-derived from font-size with font-optical-sizing: auto
- Added class for styling keyboard shortcuts

### 1.30.0 (250801)

- BREAKING: Changed .tab.btn to display:block
- Added pathname matching modes for NavIndex
    - Allows for handling subpaths correctly

### 1.29.0 (250727)

- Implemented Component support for CtxMenuItem
    - BREAKING: `CtxMenuItem: Snippet` > `CtxMenuItem: { snippet: Snippet<[CtxMenuItemProps]> }`
    - Use with components: `CtxMenuItem: { component: Component<CtxMenuItemProps> }`

### 1.28.1 (250727)

- Had to unwrap further to reach intended goal of v1.28.0: Also removed li wrapper
    - Either use: `li > button`, `li > button + button` repeated as often as the entries you need
    - Or use: `div`, `section`, `p` for uniform block padding and rounded coners
    - Or use your very own setup

### 1.28.0 (250727)

- BREAKING: Removed div.snippet wrapper for `CtxMenuItem Snippets`
    - To keep previous styling, wrap your Snippet in `div`, `section` or `p`
    - Allows for using button, which will be identical to native CtxMenuItem actions
    - Allows for using a reactive Svelte map of multiple buttons

### 1.27.0 (250726)

- Improved ctx-menu opening and closing logic
    - Ignore right-clicks inside context menu
    - Close if open and would open the exact same menu anchored to the same element

### 1.26.0 (250726)

- Implemented workaround for ctx-menu instantly closing again on mobile
- BREAKING: Renamed `ContextGlobal` to `CtxMenuRoot`

### 1.25.0 (250724)

- Implemented left-click CtxMenu
    - Use: onclick={openMenu(menu)}
- Fixed CtxMenuTagMap for disabled menus
- Updated dependencies

### 1.24.0 (250724)

- Disabled pointer events for SVGIcons that have an icon-root
    - Fixes context menu global button handling for icon buttons
- Made callback in clipboard action helpers optional
- BREAKING: Context menu renames
    - type MenuConfig > CtxMenuConfig
    - type MenuItem > CtxMenuItem
    - class ContextMenu > CtxMenu
    - type ContextMenu['tagMap'] > CtxMenuTagMap
- Allowed non-checkers on CtxMenuTagMap
    - CtxMenuOpeningAction can be used as well

### 1.23.0 (250724)

- Added input context menu clipboard action helpers: copy, cut, paste

### 1.22.0 (250723)

- Improved ToggleLink and ToggleLock setup
    - Fixed sizing issues
    - Migrated to SVGIcon snippet syntax
- Added styling for svg containing .toggles and .radios, so they fit in line with text and number inputs inside .input-groups
    - Used by ToggleLink and ToggleLock
- Corrected \[disabled] to :disabled in the global stylesheet
- Removed forced id generation for all input components
    - Not neccessary, since input is child of label
    - Name can either be passed or not, id is no longer used as name fallback
- BREAKING: Removed highlight prop from Radio, just pass it as the class

### 1.21.0 (250722)

- Improved NumberInput styling
    - Correct cursor for buttons
    - Correct button style on disabled state

### 1.20.0 (250721)

- BREAKING: Removed `.active` class from `Icon`
    - Only :active pseudo-class will be listened to from now on
    - For creating alternative theming for an activated state, the color reactivity should be handled by the parent component
    - Both :hover and :active might be different values on an activated state
    - ActionButton was adapted accordingly

### 1.19.1 (250718)

- Fixed NavIndex, hidden text was visible due to icon-btn change

### 1.19.0 (250718)

- Added /transitions, specifically flexSlide

### 1.18.0 (250718)

- Added .btn.icon style
- Deprecated .btn.icon-btn class, this works automatically now
    - Remove any .icon-btn classes in your repo

### 1.17.1 (250718)

- Fixed recent dirs not being added to package.json to allow importing

### 1.17.0 (250718)

- Added scrollbar styling to global stylesheet

### 1.16.0 (250717)

- Added context menu logic and component
    - Added action-list to global stylesheet

### 1.15.0 (250715)

- BREAKING: Changed input components, so that class and style apply to wrapper
    - Other atts still apply to the input element itself
    - Changed: `NumberInput`, `Radio`, `Toggle`, `ToggleLink`, `ToggleLock`
    - NumberInput had workaround `cls`, which was replaced with `class` again

### 1.14.1 (250715)

- Fixed current location matching for both Tabs and NavIndex
- Docs changes
    - Fixed default minimized state
    - Fixed output for paths, so reloading works on web server

### 1.14.0 (250714)

- Added NavIndex minimized state, with smooth transition

### 1.13.1 (250713)

- Fixed CodeBlock overflowing and breaking layout

### 1.13.0 (250713)

- BREAKING: Separated font-faces from global style sheet
    - You need to import `@leon8ix/jhana-ui/fonts/inter.css` to keep Inter font as before
- Implemented CSS variables for
    - Monospace font
    - Variable font feature and variation settings
- Added bold class to CSS and made `b` and `strong` bold as well
- Improved CodeBlock styling

### 1.12.0 (250712)

- Replaced button texts in CodeBlock with icons

### 1.11.0 (250712)

- BREAKING: Transitioned to flexible `SVGIcon` syntax for all icons
- Added CodeBlock component

### 1.10.2 (250625)

- Fixed NumberInput up/down arrows causing weird numbers (rounds to 12 digits now)

### 1.10.1 (250625)

- Fixed NumberInput disable, applies to up/down arrows as well now

### 1.10.0 (250612)

- Updated all dependencies

### 1.9.0 (250612)

- Changed notify to allow for replacing noti data while open using id

### 1.8.0 (250612)

- Switched over to use Bun instead of NPM

### 1.7.0 (250612)

- Replaced internal utils functions with @leon8ix/utils
    - Auth token not included: Needs to be in user's home directory or consuming package

### 1.6.0 (250420)

- Added Spinner component

### 1.5.1 (250420)

- Removed SVG (and IMG etc.) reset, as it was causing problems with ToggleLock and ToggleLink

### 1.5.0 (250420)

- Made CSS reset much less aggressive
    - Basically no global selector, all default styled elements are targeted specifically
        - Previously global styles would overwrite any SVG's presentation atts
        - Only padding and margin are still global
    - Global 'outline: none' removed for accessibility
        - Only classes that specifically handle :focus-visible have 'outline: none'
    - Tested with
        - Infinipose 3.34.0-pre.4
        - Cover Designer 2.0.0-pre.11

### 1.4.1 (250417)

- Fixed font shorthand overwriting variable font-weight

### 1.4.0 (250417)

- Improved CSS reset in the stylesheet
- Updated dependencies

### 1.3.0 (250416)

- Added keyboard shortcut Svelte action

### 1.2.4 (250315)

- Fixed ActionButton class and color logic

### 1.2.3 (250315)

- Fixed missing unit for space-y-sm

### 1.2.2 (250312)

- Fixed NotifyRoot fixed positioning

### 1.2.1 (250309)

- Added /notify entrypoint

### 1.2.0 (250309)

- Added notification system

### 1.1.2 (250307)

- Fixed btn with non-button tags not having browser style for button line-height: normal

### 1.1.1 (250305)

- Fixed action-btn svg still depending on px unit

### 1.1.0 (250305)

- Replaced rem with em, as rem would cause problems on dynamic font size change

### 1.0.1 (250302)

- Fixed style.css import not working for consuming package

### 1.0.0 (250302)

- Initial commit
- Extracted out from Print Meter v4.0.0
- Before being used for and adapted to Print Meter, it was used in Infinipose
- Before being used for and adapted to Infinipose, it was developed for IconBase