# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.6.0] - 2026-09-16

> Minor. A **heading series** — `qo-h1` … `qo-h6` — and the scale behind it,
> restated in px for a back-office density. Additive except for the six
> `$font-size-h*` tokens, which change value; read the behaviour note.

### Added — `qo-h1` … `qo-h6`

The design system had no heading classes. Each new one carries **size, leading
and weight together**, because those three are what make a heading a heading:
H4, H5 and H6 sit at or below the body size, so weight is the only thing
distinguishing them, and a `qo-font-*` class — one step of the size ramp and
nothing else — cannot express them.

Generated from `$heading-sizes`, a level-keyed map beside `$spacers`, so the
series and the tokens cannot drift. Both tiers the theme generates for every
utility are present, including the `[data-prc-col-important~="qo-h3"]` twin —
which is what lets a schema set a heading's *appearance* independently of its
*level* (see `H1` … `H6` in `procode-lowcode-kendo-widget` 2.7.0).

**Deliberately no margins.** Every other utility in that file sets only the
properties it names, and a heading's spacing belongs to the layout it sits in —
compose with `qo-mb-*`.

### Changed — the heading scale is px, and sized for this application

| | 2.6.0 | was |
|---|---|---|
| `$font-size-h1` | **20px** | `2.5rem` (40px) |
| `$font-size-h2` | 18px — `$font-size-xl` | `2rem` (32px) |
| `$font-size-h3` | 16px | `1.75rem` (28px) |
| `$font-size-h4` | 14px — `$font-size-lg` | `1.5rem` (24px) |
| `$font-size-h5` | 13px — `$font-size-md` | `1.25rem` (20px) |
| `$font-size-h6` | 12px — `$font-size-sm` | `1rem` (16px) |

The old ladder was Bootstrap-shaped, sized for an article on a page of 16px body
text. This is a back-office application whose body size is 14px and whose
densest text is 11px; a 40px H1 there is a banner, not a heading — which is why
**no package ever used one of these tokens**. Four of the six steps are now the
existing size ramp; only H1 and H3 carry a size of their own.

New alongside them: `$line-height-heading` (1.3) and `$font-weight-heading`
(`$font-weight-semibold`), one of each for all six rather than a pair per step —
a heading is short, so per-size leading buys nothing and is six more values to
keep in sync. Both are exposed as `--qo-line-height-heading` /
`--qo-font-weight-heading`, so an app styling a heading by hand lands on the
same three values the utility does instead of guessing two of them.

### Behaviour changes that can break an existing screen

- **`--qo-font-size-h6` was 16px and is now 12px.** `vsbackoffice`'s
  `copilot-procode.scss:92` reads it expecting 16px and will shrink. Point that
  rule at `$font-size-h3` (16px), or restore the old value in one line before
  the theme import — every token is `!default`. The other five custom properties
  move too, but nothing in any package or in the app reads them.
- The bare `h4 { font-size: 13px }` element rule is **unchanged** and still
  contradicts `$font-size-h4` (14px). It is unrelated to `qo-h4`; an `<h4>` from
  the `H4` widget carries the class (0,1,0) and beats the element rule (0,0,1),
  but a bare `<h4>` in app markup does not.


## [2.5.1] - 2026-09-15

> Patch. Four entries. Three are the disabled treatment — the edge becomes
> visible again, the class form matches the attribute form, and the vendor-
> specific half moves to the package that owns the vendor. The fourth is that
> move itself: **this package no longer contains a single Kendo selector.**
> No token, class or measure is added or removed.

### Changed — every `.k-*` selector leaves this package

`components/_disabled.scss` named `.k-disabled`, `.k-input` and `.k-picker`.
They were the only Kendo references in vs-theme, and they should not have been
here: this package is the design system, consumed by applications that draw no
Kendo at all, and a vendor selector makes every one of them carry a markup
contract they never render. It is the rule vs-template already follows — "must
not import `@progress/*`, route all Kendo usage through the standard widget
types".

The Kendo half now lives in `procode-lowcode-kendo-widget`
(`src/assets/styles/_kendo-bridge.scss`, 2.5.0), which also binds Kendo's whole
colour map to the tokens defined here. **Adopt the two together**: on this
version alone, a disabled Kendo field loses its treatment, because the element
Kendo marks is a `<span class="k-disabled">` with no `disabled` attribute for
the rule below to match.

What stays here is the state itself — `:disabled`, `[disabled]`, `.qo-disabled`
— stated once, in tokens, with no vendor vocabulary anywhere in it.

### Changed — the disabled edge is one step darker than the fill

2.5.0 painted the border in `$background-disabled`, the same token as the fill,
on the reasoning that the edge should move with the grey if the grey is ever
retuned. That removed the live accent colour, which was the bug — but it also
removed the control. A disabled field became a grey rectangle with no
boundary, and on a tinted panel (`$background-light`, `$background-header`) it
stopped reading as a field at all.

```
border-color  $background-disabled #d9e0e7  ->  $border-secondary #c3ccd6
```

`$border-secondary` is the token the enabled control already hovers to, so the
disabled edge is a measure the system owns rather than a new value, and it
stays clearly quieter than the `$border-primary` focus ring.

### Moved — the composite-control fix ships from kendo-widget instead

A disabled DatePicker, TimePicker, NumericTextBox, ComboBox or MaskedTextBox
rendered a near-white `#f5f5f5` button block and a `rgba(0,0,0,.08)` hairline
inside the grey field: `.k-input`/`.k-picker` delegate the toggle, spinner and
clear affordance to child elements, and Kendo marks only the wrapper. That fix
was written here first and has moved, unchanged in behaviour, to
`procode-lowcode-kendo-widget` 2.5.0 — see that changelog for the mechanism.

### Fixed — `.qo-disabled` and `[disabled]` looked like two different states

The class form of the state was handled component by component:
`.qo-input.qo-disabled` set a fill but no border, `.qo-btn.qo-disabled` set only
`opacity: 0.48` — so a solid button switched off by class stayed a faded blue
while the same button switched off by attribute went grey. `.qo-disabled` joins
the global selector and the two forms are now identical.

**Adoption:** no API change. **Every** disabled control changes appearance —
the edge is visible again — and screens with a disabled `.k-input`-family
widget or anything using `.qo-disabled` change further. That is the fix.
Anything deliberately overriding the old look needs `!important` plus higher
specificity, as before.

## [2.5.0] - 2026-09-11

> Minor. Three entries: two spacing steps move back onto the 4px grid, the
> global disabled treatment stops leaving white labels and accent borders on its
> own grey fill, and the scrollbar treatment that lived in one vs-template
> widget becomes the stack's single rail. No class or token is removed.

### Fixed — two spacing steps were off their own grid

`base/_space.scss` opens by defining the scale as "a strict 4px grid where the
index is the multiplier", and the 2.3.0 changelog entry that introduced it lists
`2 -> 8px, 4 -> 16px, 5 -> 20px`. The values that shipped were `10px`, `14px`
and `16px` — three steps, each chosen independently, none agreeing with the
documentation or with each other.

```
$space-2:  10px -> 8px
$space-5:  16px -> 20px
$space-4:  14px  (held deliberately — see below)
```

**Step 4 stays at 14px, off the grid, on purpose.** 14px is the measure the
design wants there: `$space-lg` is built on it and is the most-used spacing in
`procode-vs-template`, where 16px reads as too loose. The resulting ladder is

```
0 -> 0   1 -> 4   2 -> 8   3 -> 12   4 -> 14   5 -> 20   6 -> 24   8 -> 32
```

so every step but one is a multiple of 4. That is a documented exception rather
than a drift, and it is the difference this release is actually making: three
values that disagreed by accident become one value that differs by decision.

The t-shirt aliases follow:

```
$space-sm  10 -> 8     (was 5px before 2.3.0)
$space-lg  14          (was 16.5px)  unchanged from 2.4.0
$space-xl  16 -> 20    (was 21px)    <- closer to the authored value
$space-xxl / $space-xxxl unchanged at 24 / 32
```

**Adoption:** anything using `--qo-space-2` or `-5`, or the `qo-gap-*`,
`qo-padding-*` and `qo-margin-*` utilities built from them, moves by 2-4px.
`$space-1`, `-3`, `-4`, `-6` and `-8` do not move.

### Fixed — a disabled button kept its white label and its live accent border

`components/_disabled.scss` has always repainted every disabled control with
`background-color: $background-disabled !important` (#d9e0e7). It forces the
fill because it has to beat a component's own: `.qo-btn.qo-btn-primary` is
(0,2,0) where `*:disabled` is (0,1,0).

It did not force the colour. A solid variant sets `color: $text-light` to be
legible on its own coloured fill, so the moment this rule replaced that fill
with grey and left the text alone, the label was white on #d9e0e7 — invisible.
Advanced Search's "Apply Filter" is disabled until the filter is valid, so it
spent most of its life unreadable; every `qo-btn-<variant>`, `qo-btn-soft-*`
and Kendo `k-button-solid-*` in a disabled state had the same problem.

```scss
*:disabled,
*[disabled],
*.k-disabled {
  background-color: cl.$background-disabled !important;
  border-color: cl.$background-disabled !important;   // added
  color: cl.$text-secondary !important;               // added
  cursor: not-allowed !important;
}
```

`$text-secondary` (#667085) rather than `$text-default`: it reads as clearly
muted against the enabled `$text-dark` while still holding ~3.7:1 on the
disabled grey, where `$text-default` sits nearer 2.6:1 and starts to disappear.

`border-color` had the same gap for the same reason. Every variant states its
border alongside its fill — `.qo-btn.qo-btn-primary` sets #1261e8, `-danger`
#ef4444 — so greying only the inside left a disabled button ringed in its live
accent colour, and on `qo-btn-outline-*` that ring was the whole control. A
blue-outlined grey block reads as available, which is the one thing a disabled
control must not do. The edge now takes `$background-disabled` rather than
`$border-default`: both are #d9e0e7 today, but tying the border to the fill's
own token means a future retune of the disabled grey moves them together
instead of leaving a seam.

**Adoption:** nothing to change. A host rule that was compensating for the white
label — a local `color: … !important` on a disabled control — is now redundant
and should be deleted, because it will beat this one.

### Added — `utilities/_scrollbar.scss`, one rail for every scroll container

A 4px rail, transparent track, `$border-default` thumb at `$border-radius-md`
darkening to `$border-secondary` on hover. This is `procode-vs-template`'s
column-chooser treatment promoted verbatim — it was the only scrollbar in the
stack anyone had designed, and it was reachable only inside that one widget.

Everywhere else fell back to the platform scrollbar: ~15px wide on Windows, a
different colour on every OS, and sitting next to the 4px rail wherever a
chooser pane shared a screen with a grid. vs-template additionally carried two
more near-copies that had already drifted from the chooser (a hard-coded 2px
thumb radius, no track, no hover state).

The rule is applied through `*, html, body` rather than a list of containers,
because the containers are not all ours — Kendo's grid, popups and dropdown
lists each open their own scroll boxes and none carries a class this package
could name.

- `.qo-scroll` — the same treatment as an opt-in class, for a container whose
  own `::-webkit-scrollbar` block would otherwise outrank the `*` rule.
- `.qo-scroll-auto` — escape hatch back to the platform scrollbar.
- `$scrollbar-size`, `$scrollbar-track`, `$scrollbar-thumb`,
  `$scrollbar-thumb-hover`, `$scrollbar-radius` are `!default`, so a consumer
  can retune the rail without forking the rule.

Firefox has no pseudo-element API for this; it gets `scrollbar-width: thin` and
`scrollbar-color`, which is the whole surface it exposes.

## [2.4.0] - 2026-09-11

> Minor and **purely additive at the class level** — 473 new classes, **none
> removed**. 2.3.0 aligned the *tokens* with the VARStreet listing-page
> specification; 2.4.0 builds the Bootstrap-shaped **component layer** on top
> of them, so a screen composes `qo-btn qo-btn-primary` instead of assembling
> a button out of utilities. Every component carries its own `:hover`,
> `:active`, `:focus-visible` and `:disabled` states — there are no separate
> state classes to add.

### Added — `base/_variants.scss`, the single source of truth

Buttons, badges, alerts, cards, links and inputs all loop over one
`$variants` map. Each entry carries `base` / `hover` / `active` / `on` /
`soft` / `ink`, so a new semantic colour is declared once and every component
picks it up. `hover` and `active` are pre-computed there rather than in each
component, so the two can no longer drift, and a design token
(`$text-primary-hover`, `$text-danger-hover`) always wins over the computed
−8 % / −12 % step.

`$status-variants` holds the tint-only entries — `standard`, `lite`,
`system`, `custom`, `active`. These name *a state of a record*, not a
component colour: there is no solid "system role" button, so they get `soft`
and `ink` and nothing else.

### Added — components

| Component | Classes | Notes |
|---|---|---|
| Buttons | `qo-btn` + `-<v>`, `-outline-<v>`, `-soft-<v>`, `-link-<v>` | 4 families × 10 variants |
| Button sizes | `qo-btn-xs\|sm\|md\|lg`, `-icon`, `-full`, `-pill`, `-square` | `-icon` is square at every size |
| Button group | `qo-btn-group` | Adjacent buttons share one outline |
| Badges | `qo-badge` + 15 tint, 10 solid, 10 outline variants | `-sm\|md\|lg`, `-square`, `-fixed`, `-action`, `qo-badge-dot` |
| Forms | `qo-input` (+ `-xs\|sm\|md\|lg`), `qo-select`, `qo-check`, `qo-label`, `qo-help`, `qo-field` | Validation states `-success\|warning\|danger\|info` |
| Input group | `qo-input-group`, `qo-input-icon-start\|end`, `-search` | Icon is positioned, not padded in by hand |
| Cards | `qo-card` (+ accent per variant), `qo-card-header\|body\|footer` | `qo-metric-card`, `qo-metric-icon` (any tint), `qo-metrics-grid` |
| Data grid | `qo-grid-shell`, `qo-grid-scroll`, `qo-table`, `qo-table-header\|filter`, `qo-table-link\|meta\|sort` | `-striped`, `-compact`, `-borderless`, `qo-table-row-locked` |
| Pagination | `qo-pagination`, `qo-page-btn` (+ `-active`), `qo-page-size` | |
| Tabs | `qo-tabs` (+ `-sm`, `-pill`), `qo-tab` (+ `-active`) | Underline drawn on `::after` so the tab does not shift 2px when activated |
| Alerts | `qo-alert` + 15 tint, 10 outline, 10 accent variants | `qo-alert-icon\|title\|close` |
| Page shell | `qo-page`, `qo-page-header\|title\|subtitle\|action`, `qo-toolbar` (+ `-between`, `-end`, `-wrap`), `qo-toolbar-search` | Responsive at 1200px / 768px per the spec |
| Links | `qo-link` + per-variant, `-muted`, `-underline`, `-plain` | |

### Added — interaction utilities (`utilities/_interaction.scss`)

- `qo-focusable` applies the theme's single `$focus-ring` to anything;
  `qo-clickable` is the whole pointer affordance; `qo-hoverable` is elevation-only.
- `qo-shadow-none|sm|md|lg|xl` — the token-named aliases of `qo-boxshadow-*`.
- `qo-h-control`, `-control-sm`, `-badge`, `-grid-header`, `-grid-filter`,
  `-grid-row`, `qo-min-h-control` — so a bare element lines up with a control.
- `qo-sr-only` — the spec requires icon-only buttons to carry a real label.
- A `prefers-reduced-motion` block that neutralises every transition the
  component layer introduces.

### Fixed — two defects that were in the register

- **`qo-btn-danger` was red-on-red.** `color` was `$text-danger` over a
  `$background-danger` fill. It now uses the variant's `on` colour (white),
  and `:hover` darkens the fill instead of washing it out to a 6 %-alpha
  transparency.
- **`qo-btn-outline-default` had invisible text.** It set `color: $text-light`
  (white) on a transparent background, so the label only appeared on hover —
  which stayed hidden because six of its seven uses were icon-only buttons.
  Outline variants now take the variant's `ink` colour, which is legible on a
  white page by construction.

### Notes

- **Focus is `:focus-visible`, not `:focus`.** A mouse click does not leave a
  ring behind, which is why hand-rolled `:focus` rings were being deleted
  screen by screen. Keyboard focus is never suppressed.
- **The global `*:disabled` rule still wins.** `components/_disabled.scss`
  sets `background-color: $background-disabled !important` on every disabled
  element, so a disabled `qo-btn-primary` renders grey rather than a faded
  blue. That rule predates this release and was left alone.
- `qo-select` uses `@extend .qo-input`, and `.qo-input-group` uses `:has()`
  for its trailing-icon padding — Chrome 105+, Safari 15.4+, Firefox 121+.

## [2.3.0] - 2026-09-11

> Minor, but **every screen changes appearance**. This aligns the theme with the
> VARStreet listing-page specification (`Resprces/varstreet-listing-page.css`).
> 48 token values move and 44 tokens are added; **no token or utility class is
> removed**, so nothing breaks by reference — but sizes, spacing, radii and
> colours all shift. Read "Behaviour changes" below before adopting.

### Changed — scales restructured as whole ladders

A scale is only coherent if it moves as a set, so where the spec redefined one
step, the whole ladder was re-derived rather than spot-patched.

- **Font sizes.** The spec supplies six steps (11/12/13/14/18/22) and the ladder
  has six slots above `xxs`, so each spec value lands on exactly one existing
  name: `xs` 10→11, `sm` 11→12, `md` 12→13, `lg` **14 (unchanged)**, `xl` 16→18,
  `xxl` 18→22, `xxs` 8→10. `lg` holding at 14px is what makes this survivable —
  it is both the most-used step in the consuming app and the spec's body size.
  `$font-size-base` is added as an explicit alias of `lg`.
- **Spacing.** Replaced the non-linear Bootstrap rem ladder with the spec's
  strict 4px grid, where the index is the multiplier: `1`→4px, `2`→8px,
  `3` 16→12px, `4` 24→16px, `5` 48→20px, plus new `6` (24px) and `8` (32px).
  Steps 1 and 2 were already correct and do not move.
  The t-shirt aliases are now the same ladder instead of a parallel off-grid
  one: `xs` 2.5→4, `sm` 5→8, `md` 12.5→12, `lg` 16.5→16, `xl` 21→20,
  `xxl` 28→24, `xxxl` 30→32 — every one moves 3px or less.
- **Border radius.** `sm` 2→4, `md` 4→6, `lg` 16→8 per the spec. `xl` is now
  16px, which also resolves a latent duplication: `lg` and `xl` were both 16px.
  `pill` is 999px rather than 50rem (identical rendering).
- **The neutral ramp is now cool.** The spec's greys are blue-tinted where the
  previous ones were flat, and mixing the two ramps is what makes a
  half-rebranded screen look dirty, so they were replaced as a set:
  `#f7f7f7`→`#f7f9fc`, `#f4f4f4`→`#f8fafc`, `#e3e3e3`→`#e9edf2`,
  `#d9d9d9`→`#d9e0e7`, `#808080`→`#7b8492`, `#595959`→`#667085`,
  `#464646`→`#273142`, `#333333`→`#111827`.
- **Brand and status colours.** primary `#2666e3`→`#1261e8` (hover
  `#1c428d`→`#0d4fc4`), success `#5ca224`/`#67b926`→`#49a51a`, danger
  `#f90b0b`/`#e74c3c`→`#ef4444`, warning `#f39c12`/`#f97d0b`→`#ef8a00`, info
  `#17baff`→`#164ea3`, placeholder `#808080`→`#8a94a6`.

### Added

- **Soft/tint background family** — `$background-{primary,success,warning,danger,info}-soft`
  plus `-lite-soft`, `-system-soft`, `-custom-soft`, with matching
  `qo-bg-*-soft` classes. Badges, banners and icon chips had no pale fill to
  use and were hand-colouring hex values screen by screen.
- **`$focus-ring`** (`--qo-focus-ring`). The stack had no focus token at all;
  several screens hand-rolled `box-shadow: 0 0 0 …`.
- **Shadow tokens** `$shadow-sm|md|lg|xl` (`--qo-shadow-*`) on the spec's
  cool-neutral hue. The `qo-boxshadow-*` utilities now consume these.
  `$boxshadow-primary` is retained as the shadow *colour* token.
- **Component heights** — `$control-height` (40px), `$control-height-sm` (32px),
  `$grid-header-height` (44px), `$grid-filter-height` (48px),
  `$grid-row-height` (58px), `$badge-height` (24px).
- **Badge/status text** `$text-lite`, `$text-system`, `$text-custom` and
  `$border-primary-soft`, `$background-hover`, `$background-header`.
- **Missing utility classes** for tokens that already existed but were
  unreachable from JSON: `qo-border-default`, `qo-bg-disabled`,
  `qo-text-placeholder`.

### Fixed — values that existed but were never emitted

Eleven SCSS values were declared and then not exposed as custom properties, so
JSON and inline styles could not reach them. All are now emitted:
`--qo-line-height-{xxs…xxl}` (+ new `-base`), `--qo-space-xs`,
`--qo-text-primary-hover`, `--qo-text-danger-hover`, `--qo-bg-lime`,
`--qo-bg-transparent`, `--qo-menu-size`.

- **`--qo-text-muted` added as an alias of `--qo-text-secondary`.** The
  consuming app references `--qo-text-muted` in 6 places against a variable the
  theme never defined, so those declarations silently resolved to nothing.

### Behaviour changes that can break an existing screen

- Any screen using `qo-font-md` / `--qo-font-size-md` gains 1px; `qo-font-xxl`
  gains 4px. `qo-font-lg` is unaffected.
- `--qo-space-3` loses 4px and `--qo-space-4` loses 8px — the two heaviest
  non-matching spacing steps. `--qo-space-5` drops from 48px to 20px, which is
  the largest single move in this release; check anything using it for layout.
- `--qo-border-radius-lg` halves (16→8px).
- **Hardcoded hex values do not follow the tokens.** The consuming app contains
  675 hardcoded colour literals, led by `#2666e3` ×61 (the old primary),
  `#464646` ×35 and `#808080` ×22. Until those are swept onto tokens, adopting
  this version produces a half-rebranded UI. This sweep is the real adoption
  cost of 2.3.0, not the version bump.
- Seven further `--qo-*` names are referenced by the app but defined nowhere
  (`--qo-border-color` ×13, `--qo-border-radius` ×3, `--qo-primary` ×2,
  `--qo-font-sm` ×2, `--qo-bg-muted` ×2, `--qo-spacing-md` ×1,
  `--qo-line-height-md` ×1 — the last now resolves). They remain broken and
  should be corrected app-side.

## [2.1.0] - 2026-08-18

> Minor: both entries are additive — new tokens, no existing value changed.

### Added
- **Dialog-scale spacing: `$space-xxl` (28px) and `$space-xxxl` (30px).** The
  legacy alias scale topped out at `$space-xl` (21px), which is too tight for
  the outer padding of the larger modals — the column chooser wanted 28/30 and
  had nowhere on the scale to get it. Both are in `$spacers`, so the
  `qo-p-*` / `qo-m-*` / `qo-gap-*` utilities pick them up, and both are exposed
  as `--qo-space-xxl` / `--qo-space-xxxl`.
- **`$text-darker` (#333333).** `$text-dark` (#464646) was the darkest text
  token, but dialog headings in the design sit a step below it — measured off
  the column-chooser reference screenshot, whose title renders at #333333.
  Exposed as `--qo-text-darker` and the `qo-text-darker` utility.

  ⚠️ **Cross-package coupling:** `procode-vs-template` now references these in
  `GridToolBar/scss/index.scss`. An app pinning an older `procode-vs-theme`
  alongside that vs-template will fail to compile with "Undefined variable".
  Ship the two together.

## [1.0.0] - 2025-10-08

### Added
- Initial release of VarStreet VS Theme Package
- Comprehensive SCSS theme system with modular architecture
- Multiple export endpoints:
  - Main theme export for complete theme access
  - `/variables` for design tokens and theme variables
  - `/components` for component-specific styling
  - `/layout` for layout and grid system
  - `/utilities` for utility classes and helpers
  - `/scss` and `/styles` for direct SCSS file access
- Theme configuration interface and utility functions
- Full TypeScript support with comprehensive type definitions
- Base styling system with typography, colors, and foundational styles
- Component-specific styling for buttons, forms, and UI elements
- Flexible layout system with grid, flexbox, and positioning utilities
- Comprehensive utility classes for spacing, colors, and responsive design