@guildofgleks/ui
Releases
The changelog shipped inside the package this site was built against — v21.4.4. It is read from node_modules at build time rather than copied here, so it cannot describe a version different from the one documented on every other page.
All notable changes to @guildofgleks/ui are documented here. Format follows
Keep a Changelog; this project has not yet
reached 1.0, so breaking changes may land in minor versions.
planned
The breaking release — the one version consumers have to read before upgrading into. It
carries the removals below and, per docs/hardening-21.5.0.md, the token-prefix rename
(--gog-ms-*, --gog-btn-*, --gog-input-* spelled out in full). Everything non-breaking that
is ready today ships in 21.4.4 instead, so a reader upgrading to 21.4.4 has nothing to migrate
and a reader upgrading to 21.5.0 has one list to work through rather than one buried among fixes.
Removed
Not done yet — this section records what the version owes. Fourteen public symbols carry an
@deprecated … Removed in 21.5.0 tag naming its own replacement:
- the six legacy icon inputs on
gog-inputfield; - the
checkIconTemplate/clearIconTemplate/iconTemplate/chevronTemplateinputs; - the
<column>element, with itsColumnconst and type; - the string-keyed
[template]column slot; - the
GogSelectOption/GogMultiselectOptionaliases — these two overran their announced 21.4.0 removal, which is the reason the date on this list is not moved again.
One more is promised in prose rather than by a tag, so it has no @deprecated to grep for and is
the one most likely to be missed:
- the
./src/styles/*export inpackage.json. Stylesheets moved to./styles/*in 21.3.2 and the README says the old path "keeps working until 21.5.0". Removing it breaks anyone still importing@guildofgleks/ui/src/styles/…, and the README paragraph promising it goes too.
17.08.2026
Everything that is ready. Two defects and one addition, none of which changes an existing
signature, so upgrading from 21.4.3 needs no migration — see the gog-collapsible entry only if
you were relying on a panel being capped at 480px.
Added
ng add @guildofgleks/uinow works. It installs the latest version and addsnode_modules/@guildofgleks/ui/styles/index.cssto your project'sangular.jsonstyles — the one setup step that's pure mechanical JSON editing. Importing components and placing<gog-dialog />/<gog-toast-container />are still manual; see the README.In a patch rather than a minor because it is purely additive — it adds a way to install the package and touches no existing API — the same reasoning that put "
CHANGELOG.mdnow ships inside the package" in 21.4.2.
Fixed
An open
gog-collapsibleno longer clips content taller than 480px.--gog-collapsible-max-heightdefaulted to480pxand the panel isoverflow: hidden, so any panel taller than that lost the rest of its content — with no scrollbar, no ellipsis and nothing else to indicate it. The cap existed only to give the CSS transition an animatable target, which meant every consumer had to discover the limit by having content disappear, then override the token per instance. The default is nowmax-content, andinterpolate-size: allow-keywordson the panel keeps the open/close transition animating to it.gog-accordionnever had this problem — it animatesgrid-template-rowsinstead — so the two components now behave the same way.No token was added or removed, and nothing about the closed state changed. Setting
--gog-collapsible-max-heightto a length still caps the panel and still clips, which is now a deliberate opt-in rather than the default; per-instance overrides that only existed to work around the old cap can be deleted.gog-button's loading spinner was painted with the page's text colour.--gog-btn-primary-spinner-colorand--gog-btn-secondary-spinner-colorresolved to--gog-text-color— the colour of text on the page background — while the label beside the spinner used--gog-accent-text-color, the colour meant to sit on the button's fill. On the two filled variants those are opposite ends of the palette, so the spinner came out washed out: on the dark theme,#f3ebd8parchment on a#fbbf24amber button, next to a near-black label. Both now resolve to the variant's own label colour, so a spinner reads exactly as strongly as the text it replaced and follows any re-theming of the button's foreground.outlineandghostwere already correct and are untouched.Only the two tokens' values changed; no token was added or removed. A theme that sets either one explicitly is unaffected. Note the button as a whole still dims to
--gog-btn-loading-opacity(0.7) while loading — that is deliberate and separate from this.
16.08.2026
Two components stopped fighting their container. Nothing was added or removed from any public
API, but gog-calendar and gog-datepicker [inline] are narrower than they were — see the
first entry if you were relying on a calendar filling its container.
Changed
gog-calendarcaps itself at its own month grid. Its host was a block with no width, so in any container wider than the grid the header — a flex row carrying the title and its six nav chevrons — stretched the full width while the days huddled at the start. Every consumer placing a calendar in a plain container had to write a width by hand to get a calendar that looked like one. The new--gog-calendar-max-width(defaultmax-content) does it once:max-contentneeds no numbers and already tracks the size variants,numberOfMonths,showTimeand locales whose weekday abbreviations run wider than English's. It is a cap, so a narrower container still constrains the host.This sizes
inlinemode too —[inline]="true"rendersgog-calendarwith a border and nothing else. Set--gog-calendar-max-width: 100%to keep the old full-width behaviour.--gog-datepicker-panel-width(defaultmax-content) exposes the dropdown panel's width, which was hardcoded. Same value as before; nothing changes unless you set it.
Fixed
gog-tabsno longer scrolls the page to reach its active header. It centred the active header withElement.scrollIntoView(), which scrolls every scrollable ancestor that needs to move — so a tabs component below the fold dragged the whole page down to itself the moment it initialised, and a page with several of them landed the reader at the last one. It now scrolls its own header viewport directly: the active header still centres, nothing outside the component moves. No API change;scrollActiveIntoViewbehaves as documented, only without the side effect.
15.08.2026
Documentation only — no component, input, output or token changed, so upgrading from 21.4.1 is a version bump with nothing to migrate.
Added
CHANGELOG.mdnow ships inside the npm package, alongsideREADME.md,AGENTS.mdandTOKENS.md. It was repo-only, which meant nothing could show release notes for the exact version a reader has installed — and this file is the only source that cannot drift from the package, because it travels inside it. Read it atnode_modules/@guildofgleks/ui/CHANGELOG.md; it is also what the documentation site's releases page will render. Costs ~68 KB in the tarball and nothing at runtime.
Fixed
AGENTS.mdwas missinggog-slider's range mode.range,rangeValue(GogSliderRange),startDisabled/endDisabledandstartAriaLabel/endAriaLabelshipped in 21.3.1 but never reached the agent reference. An agent reading it concluded the slider cannot express a range and built a two-slider workaround — the API was there all along.AGENTS.mddid not mention theGOG_ICONStoken.provideGogIcons(...)was documented, but not the token it provides — which is what an app injects to read the registered set back (an icon picker enumerating it). Public since 21.4.0, undocumented until now.
AGENTS.md is the file a coding agent reads to build against this package, so a gap in it
produces code written against API that does not exist, or workarounds for API that does. Both
entries above are that kind of gap, which is why a documentation-only patch was worth cutting.
14.08.2026
Fixed
Overlays ignored custom properties set on
:root. A select panel, tooltip or any other overlay rendered into<body>copied thedata-themeof its trigger's nearest themed ancestor. When that ancestor is<html>— the usual case — the copy made the overlay matchtheme.css's derived layer (:root, [data-theme]) locally, re-declaring every component token against the plain preset palette and discarding anything set on the root that the preset does not itself declare.Inline custom properties are what this hit: a page that overrides
--gog-*ondocument.documentElement— a live theme editor, or any runtime accent switch — saw the document follow while every overlay kept rendering the un-edited theme.The attribute is now copied only for a genuinely scoped theme, where the overlay would otherwise pick up the document's; when the theme sits on the document element, inheritance already does the work. Several themes rendered side by side in scoped subtrees keep working exactly as before.
14.08.2026
A minor rather than a patch: this adds public API. Iterations 5 and 6 of the consumer-DX plan
(docs/consumer-dx-plan.md).
Added
gog-table: outputs. The component had none at all, which is what made it a display-only grid.gogSortChange({ field, direction }, including the third click that clears the sort),gogPageChange(the new 1-based page), andgogRowClick({ row, index, originalEvent }).gogPageChangedeliberately stays quiet in two cases: the initial render, and the reset to page 1 that a new sort causes — that reset is part of the sort, and a consumer refetching from both events would issue two requests for one user action.gog-table:lazy— server-driven sorting and paging. With[lazy]="true"the table stops sorting and slicingvalueand renders it exactly as handed over, treating it as the current page;totalRecordstells the paginator how many pages exist, and the two outputs are the refetch signals. Row numbering still counts from the current page, andshowTotalreportstotalRecordsrather thanvalue.length. WithouttotalRecordspagination stays hidden and the table warns in dev mode. Until now the table sorted and paged purely in memory, so anything backed by a real endpoint had to be built on something else.gog-table: row selection.selectionMode('none' | 'single' | 'multiple') plus a two-way[(selection)], always aT[]— in'single'mode it simply holds zero or one row, which is one shape to read rather than aT | T[] | nullunion to narrow. A checkbox column renders automatically (showSelectionColumnturns it off), and the header select-all appears only in'multiple'mode.The select-all covers the current page, not the whole data set — in
lazymode the table has never seen the other pages, and a control that meant different things in the two modes would be worse than either behaviour on its own.gog-table:dataKey. The field (or dot-path) identifying a row. Selection matches on it instead of object identity — without it a refetch producing new objects silently drops the selection — and it becomes the@fortrack key, so the rendered DOM survives a refetch of the same page instead of being torn down and rebuilt.gog-table:interactiveRows. Makes rows focusable and styles them as clickable, with Enter and Space activating the focused row.gogRowClickfires on a click either way; this is what stops a whole-row target from being mouse-only.[gogButton]— a link that looks like a button.gog-buttonrenders its own<button>, so it could never be a link, and a large share of buttons on a real site are navigation. The directive inverts the relationship: the element stays the consumer's, and only the look is applied.html<a gogButton routerLink="/pricing">See pricing</a> <a gogButton variant="ghost" href="https://example.com" target="_blank" rel="noreferrer">Docs</a> <button gogButton variant="outline" size="sm" type="submit">Save</button>Chosen over an
as="a"/routerLinkinput trio ongog-buttonbecause that would mean brokering the router's whole input surface through the component and taking a dependency on@angular/router— a fifth peer, and one that would break every app without a router. With the directive,routerLink,href,target,download,type="submit"and anything else keep working because they were never taken away.variant,sizeandfullWidthbehave exactly as on the component,sizeincluded in itsGOG_CONFIG.control.sizefallback. It deliberately has nodisabled(there is no such thing on an<a>) and noloading(the spinner is a projected child a directive cannot add). The selector isa[gogButton], button[gogButton], not a bare attribute, so it cannot be put on a<div>and produce something that looks clickable and is invisible to the keyboard.gog-paginator: a rows-per-page select.showPageSizeSelectturns it on (off by default — a paginator that silently grew a control would change every existing layout) andpageSizeOptionssets the choices, defaulting to[10, 20, 30, 40, 50]. Both are also settable app-wide through the newGOG_CONFIG.paginator, so one page can offer5, 10, 20while the rest of the app uses the house default.gog-paginator:pageSize(amodel) andtotalRecords. GiventotalRecords, the paginator derives the page count frompageSizeitself — which removes thecomputed(() => Math.ceil(total / size))a consumer would otherwise have to write and keep in sync with the select.totalPagesstill works and is right when a server hands you a page count directly;totalRecordswins if both are set. Changing the size returns to page 1: "page 5" of 10-row pages is not "page 5" of 50-row ones, so clamping alone would leave the user somewhere they never asked to be.gog-table:showPageSizeSelect/pageSizeOptions, forwarded to its paginator, andGOG_CONFIG.labels.rowsPerPagefor the select's accessible name.GOG_CONFIG.labels:total,tablePagination,selectRow,selectAllRows. The table's own chrome — the row-count label readTotal:from a hardcoded string, and its paginator was labelledTable paginationwith no way to change either.provideGogIcons(...)— register your own icons by name.gog-iconshipped a closed set of 20 glyphs, and the only way to render anything else was aTemplateRefper instance, which costs an<ng-template>at every use site and does not work at all for the components that take an icon name (gog-tag,gog-chip,gog-tabs,gog-button-toggle-group,ToastService,DialogService). In practice that meant installing a second icon library — precisely the dependency the "no CDK, no Material" footprint exists to avoid.ts// app.config.ts providers: [provideGogIcons({ cart: '<svg viewBox="0 0 24 24">…</svg>' })];html<gog-icon name="cart" /> <gog-tag iconName="cart">In basket</gog-tag>- A registered name overrides a built-in of the same name, so an app can replace the library's checkmark or chevrons everywhere without touching a single component.
- Providing it again lower in the injector tree layers onto the parent set rather than
replacing it, matching
provideGogConfig. - The registry is also exposed as the
GOG_ICONSinjection token.
GogBuiltinIconName— the closed union of the shipped glyphs, for code that wants exhaustiveness (an icon gallery, aRecordkeyed by icon).21 more built-in icons, taking the set from 20 to 41. The old set covered what the library's own components needed and almost nothing an app needs: there was no
searchfor a field, notrashfor a destructive action, nomore-verticalfor a table row menu. Added, all Lucide, all on the same 24×24 / stroke-2 grid as the existing ones:- actions —
search,plus,minus,trash,pencil,download,upload,refresh,filter,external-link; - chrome —
menu,more-horizontal,more-vertical,settings; - navigation —
arrow-left,arrow-right(distinct from the chevrons, which read as disclosure rather than movement); - objects and state —
user,lock,mail,star,star-filled.
star/star-filledis the only outline/filled pair, for a rating or favourite toggle — the same casecheckbox/checkbox-checkedalready covers. The set stays outline-only otherwise: a solid duplicate of every glyph would double the payload for a distinction almost nothing needs, andprovideGogIconscovers the exceptions.Cost:
ICON_DEFSis one object, so every consumer pays for all of it — it grew from 8.0 KB to 16.5 KB raw, 1.6 KB to 2.7 KB gzipped.- actions —
Attribution for the icons. The glyphs were always Lucide but the package said so nowhere; Lucide's ISC licence asks for the notice to travel with them. It is now at the top of
icons.tsand summarised in the README's licence section.
Changed
- The button's
.gog-btn*block moved from the component stylesheet intostyles/button.css, whichstyles/index.cssimports. Angular's emulated encapsulation would never let a component stylesheet reach an<a>declared in a consumer's template, so[gogButton]needs the rules to be global — the same reasongogBadgeandgog-collapsiblealready keep theirs there. One source for both, no duplication. Costs about 1 KB gzipped in the always-loaded stylesheet; nothing changes for anyone already importingindex.css, which the Setup section has always required. npm run check:tokensnow covers the global stylesheets too. It scannedlib/**/*.scssplus a hardcodedutilities.css; it now walksstyles/*.cssas a directory, so a new global stylesheet is under the token contract the moment it exists rather than whenever someone remembers to add it. 34 stylesheets checked before, 38 now.gog-table'spageSizeis amodel, not aninput.[pageSize]="20"is unchanged;[(pageSize)]="size"is now possible, and that is what lets the rows-per-page select work with no wiring — the table binds its own model straight to the paginator's, so nothing is ferried between the two by hand.pageSizeChangecomes free from the model and is the refetch signal inlazymode.- The table footer no longer hides at a single page while the size select is on. Hiding it would strand the user on whatever size produced that one page, with no control left to choose a smaller one. With the select off, the old behaviour is unchanged.
GogIconNameis now open:GogBuiltinIconName | (string & {}). The built-ins still autocomplete; a registered name is now accepted wherever an icon name is taken, with no change at any of the ten call sites that use the type. The trade is deliberate and comes with the registry: a typo is no longer a compile error, so an unknown name renders nothing and warns in dev mode (once per name) instead of throwing — an icon is decoration, and failing a render over a glyph name would be the worse failure. Code that relied onGogIconNamebeing closed — an exhaustiveswitch,Record<GogIconName, …>— should move toGogBuiltinIconName.
Fixed
- Buttons no longer inherit the anchor underline.
.gog-btnnever resettext-decoration, because a<button>has none to reset — the moment the same block landed on an<a>via[gogButton], every link-button came out underlined. --gog-icon-stroke-widthnow applies to every shape in an icon. The rule listed onlypath,circleandrect— which happened to be all the original 20 glyphs used, so the gap was invisible. Any icon drawn withline,polylineorpolygon(half the new ones, and whatever a consumer registers) silently ignored the token and fell back to thestroke-widthattribute baked into its own markup.ellipseis covered too.
13.08.2026
First batch of the consumer-DX plan (docs/consumer-dx-plan.md, iterations 1–4): the seam
between the package and the developer installing it — setup that failed on the documented
path, accessibility that depended on optional inputs, and native attributes a wrapper component
made unreachable.
Added
- The baseline stylesheet now also ships at
@guildofgleks/ui/styles/. This is the path the README has always documented, and until now it did not exist in the package — the files were only undersrc/styles/, so a setup copied from the README failed on a missing file and every component rendered unstyled. Both paths ship for one deprecation window, andpackage.json'sexportsmap now lists them, so@import '@guildofgleks/ui/styles/index.css'resolves from SCSS as well as fromangular.json. gog-inputfield/gog-textarea: the native attribute space.readonly,maxlength,minlength,spellcheck, pluspatternandinputModeon the input.readonlydiffers fromdisabledin the usual way (still focusable, still submitted) and suppresses the clear button and the number field's spin buttons, since both offer an edit the field would refuse.autofocusis deliberately not forwarded — moving focus unasked is a documented a11y problem and the repo's own lint rule rejects it.gog-inputfield:tel,url,search,timeanddatetime-localtypes, via the new exportedGogInputType. The newGogInputModetypes theinputModeinput.GOG_CONFIG.labels. App-wide defaults for every fixed string the library renders —clear,clearSelection,clearDate,selectAll,clearAll,increment,decrement,showPassword,hidePassword,closeDialog,closeToast,pagination,previousPage,nextPage,openCalendar,today,thisMonth,previousMonth,nextMonth,previousYear,nextYear,hours,minutes,seconds, pluspage(a formatter — see Fixed). A non-English app relabels the library once instead of on every instance. Per-instance inputs still win where they exist.gog-multiselect:selectAllLabel/clearAllLabel. The panel's two buttons rendered literalSelect all/Clearwith no way to change them at all.gog-calendar:hoursLabel/minutesLabel/secondsLabel. The time section's three fields had hardcoded Englisharia-labels.GOG_CONFIG.theme.storageKeypersists the chosen theme inlocalStorage;followSystemopens in the OSprefers-color-schemesetting and keeps following it until the app callssetTheme;defaultTheme,lightThemeanddarkThemename the themes involved. All off by default, so an app that configures nothing keeps today's behaviour exactly.@angular/platform-browseris now declared as a peer dependency.gog-iconhas always importedDomSanitizerfrom it; the omission only worked because npm's flat tree hides it, and broke under strict pnpm.
Fixed
- Form controls are labelled without an
inputId.gog-inputfieldandgog-textareanow generate an id when none is given, so the<label for>actually points at the field (clicking the label focuses it, assistive tech gets a name) and the error message is reachable througharia-describedby. Previously both were silently dropped unless the consumer happened to passinputId— the default configuration was inaccessible.gog-selectalready worked this way; the id generator is now shared (gog-radio-groupandgog-slideruse it too, with unchanged output). aria-describedbyno longer points at an element that isn't rendered. It was keyed offhasError(), while the message element renders onvisibleError()— witherrorDisplay="auto"and an emptyerrorMessagethe two disagree.- Toasts are announced reliably.
aria-livemoved off the individual toast, which enters the DOM together with its own text (a live region created at the same moment as its content is routinely skipped by screen readers), onto two permanently-mounted regions ingog-toast-container— polite, and assertive forerror/warning. Individual toasts no longer carryrole/aria-live, so nothing is announced twice. gog-inputfield: the clear button on a number field wrote''instead ofnull. AFormControl<number | null>ended up holding a string, which then failed numeric validators and round-tripped the wrong type. It now writes exactly what emptying the field by hand writes.ThemeService.themeis read-only. It was a writable signal, sotheme.set(...)moved the signal without touching thedata-themeattribute the styles read, leaving the two out of sync. UsesetTheme/toggleTheme.gog-inputfield: aclearablenumber field had no clear button. The stepper and the clear button share the field's end slot, and the stepper won outright — soclearablewas silently a no-op ontype="number"unlessshowSpinButtonswas also off. Both now render: the clear button sits one stepper-width further in, and the field's text gutter widens to fit the pair (--gog-input-spin-width, new). It still disappears when there is nothing to clear, taking the extra gutter with it.gog-calendarnow readsGOG_CONFIG.datepicker.localeandfirstDayOfWeekwere documented as applying togog-calendaras well asgog-datepicker, but the calendar only ever honoured its own inputs — so a standalone<gog-calendar>in an app with an app-wide locale silently rendered inen-US. Rendered throughgog-datepickernothing changes: that component passes its own already-resolved values, which still win.gog-paginator: the per-page button names are translatable. "Go to page 4" / "Page 4, current page" were built by string concatenation in the template. They now come fromGOG_CONFIG.labels.page, a(page, isCurrent) => stringformatter — a function rather than a placeholder string, since the number's position and the grammar around it are language dependent.- The textarea resize grip's offsets are real tokens.
--gog-textarea-resize-grip-offsetand--gog-textarea-resize-inset-right/-bottomare declared intheme.cssinstead of living as literalvar()fallbacks in the component stylesheet, which the token-contract check (npm run check:tokens) had been failing on. Geometry is unchanged.
Changed
- The generated token catalogue moved from
README.mdtoTOKENS.md. It was ~200 KB of reference table in the middle of the README, burying the Setup section that a new consumer has to find within seconds on npm. The README keeps the three-layer explanation and links across; the README itself is now ~14 KB.GOG_TOKEN_GROUPSis unaffected. - README:
<gog-dialog />and<gog-toast-container />are documented.DialogService.open()andToastService.show()render nothing until those host elements are in a template, which the README never said. - README: a
## Global configurationsection.provideGogConfigwas never documented in the README at all — only individual keys mentioned in passing — so the app-wide settings, and nowlabelsandthemewith them, were undiscoverable to anyone reading the package page. Adds the key list, the precedence rule, the injector-tree merge, and a translation example. - README /
AGENTS.md: the new API is documented. Both ship inside the package.AGENTS.md(the consumer-facing agent reference) has the native attributes,GOG_CONFIG.labelsand.theme, the read-onlyThemeService.theme, generated field ids, the toast live regions, andGogInputType/GogInputModein its type table. - README: the component list is complete again. It advertised 18 components and listed 21,
while omitting
gog-autocomplete,gogBadge,gog-button-toggle-group,gog-datepicker,gog-divider,gog-progressbar,gog-tabsandgog-toggleentirely. - Label inputs that now resolve through
GOG_CONFIG.labelschanged their default from a literal string toundefined(clearAriaLabel,incrementLabel,decrementLabel,showPasswordLabel,hidePasswordLabel,todayLabel,thisMonthLabel,previousMonthLabel,nextMonthLabel,previousYearLabel,nextYearLabel,openCalendarLabel,gog-paginator'sariaLabel). Rendered output is identical unless the app configureslabels; only reading the input back in TypeScript now yieldsundefinedrather than the English default.gog-calendar'slocaleandfirstDayOfWeekchanged the same way, for the same reason.
Deprecated
@guildofgleks/ui/src/styles/…— use@guildofgleks/ui/styles/…. Both ship until 21.5.0, when thesrc/styles/copy is removed.
11.08.2026
Added
gog-slider:range. Switches the slider to two independently focusable native thumbs for picking a span instead of a single value — bind[(rangeValue)](aGogSliderRange{ start, end }pair) instead of[(value)]; the two are mutually exclusive, andwriteValue/theControlValueAccessorfollow whichever onerangeselects. Neither thumb can be dragged, keyboard-nudged, or written past the other — crossing is clamped in JS rather than through the nativemin/maxattribute, since narrowing that per thumb would desync the browser's own (invisible) thumb position from the custom--range-start-pos/--range-end-pos-driven visuals. Works in both orientations and withshowThumb/fullWidth/disabled/error display exactly as the single-value mode does.startAriaLabel/endAriaLabel(defaulting to'Minimum'/'Maximum', prefixed withlabel()when set) name the two thumbs for assistive tech, since a single<label for>can't target both. The value readout (showValue) reserves stable width up front, sized frommin()/max()/step()rather than the live value, so it — and, in afit-contentvertical slider, the whole control along with it — doesn't visibly resize on every drag.gog-slider:startDisabled/endDisabled. Disable just one thumb inrangemode — e.g. pin a range's floor while leaving its ceiling adjustable, or vice versa — instead ofdisabled, which still takes out both together. ORed withdisabledrather than overriding it, and ignored outsiderangemode (nothing to disable "one side" of there). A one-sided disable only dims and disables that one thumb (its native input's owndisabledattribute takes it out of the tab order); the whole-control.gog-slider--disabledstyling (dimming +pointer-events: noneover the whole track) only kicks in once both sides are disabled, since applying it for just one would also block pointer input to the other, still-enabled thumb. Reactive forms are unaffected by this addition: a[formControl]'s own.disable()/.enable()still speaks for both thumbs at once, same as before — oneFormControlbacks onerangeValueand has no way to target just one side of it.gog-autocomplete:openOnFocus. Focusing the field now opens the panel immediately with the full option list, ignoringminLength— the common "browse everything, then narrow it down" pattern a plain type-ahead can't offer. On by default; turn it off (or setGOG_CONFIG.autocomplete.openOnFocus = false) to keep the previous behaviour of nothing showing until enough has been typed. The list stays unfiltered even when the field already displays a previously-selected label, and normal filtering resumes on the first keystroke.gog-autocomplete:gogLoadMore. Fires once the panel is scrolled to the end of the option list — the signal to fetch and append another page, instead of handing a huge or server-backed source over up front (500,000 rows loaded 20 at a time, not all at once). Forwarded from the panel's owngog-scroll.gog-tabs:scrollActiveIntoView. With an overflowing header row, selecting a tab — by click, the arrow keys, or a consumer settingactiveIndexdirectly — now scrolls the header so the active tab stays in view, centered where there's room so its neighbours on both sides stay visible too. The same "show what's around the current position" ideagog-paginatoralready uses for pages. On by default; instant on first render, smooth (or instant underprefers-reduced-motion) after. Turn it off to own the scroll position yourself.gog-tabs:showScrollTrack;gog-scroll:showTrack. WithscrollActiveIntoViewdriving the header's scroll position, its own draggable thumb/track next to the active-tab underline read as two conflicting position indicators for the same thing — confusing rather than helpful, per feedback on the first cut ofscrollActiveIntoView.gog-tabsnow hides the track by default whilescrollActiveIntoViewis on, and shows it by default once that's off (the only way left to reach an off-screen tab by mouse); either can be pinned explicitly withshowScrollTrack, regardless of the other. Native scrolling — wheel, touch, keyboard, and any programmaticscrollTo/scrollIntoView— is unaffected either way; only the visual affordance is gone. The underlying toggle lives ongog-scrollitself asshowTrack(instance input, or app-wide viaGOG_CONFIG.scroll.showTrack), so any other panel built on it gets the same option.gog-textarea:resize. Which direction(s) the field's own drag handle resizes it in —'vertical'(the default, matching a plain<textarea>),'horizontal','both', or'none'to remove it entirely. Settable app-wide viaGOG_CONFIG.textarea.resize. The handle itself is also restyled: the browser's native glyph is barely visible at a glance, so it's blanked out (::-webkit-resizer, where that's even stylable — Firefox never exposed a hook for its own) and replaced with two short diagonal strokes in the field's own border colour, sized and positioned to sit inside the border rather than past it. AResizeObserveron the field keeps the grip glued to its actual corner as it's dragged narrower/shorter than its container ('horizontal'/'both') — it's anchored to the container, not the field itself, since a<textarea>can't reliably host::after. The drag stays entirely native; only the glyph and its tracking are new.gog-inputfield: number spin buttons. Atype="number"field now gets the library's own increment/decrement buttons instead of the browser's native ones, which render inconsistently across Chromium/Firefox/Safari and were never themed. Flush against the field's own border as one grouped stepper (a divider on each side), not floating loose in the icon gutter. Steps bystep(default1), clamps tomin/max, and disables the button at whichever boundary is reached. Arrow-key stepping on the focused field is untouched — that's native<input type="number">behaviour, unrelated to which glyphs are visible.showSpinButtonsturns them off entirely (native glyphs never come back — off means no stepper UI at all); settable app-wide viaGOG_CONFIG.inputfield.showSpinButtons.gog-icon:copy. A new glyph for the common "copy this field's value" trailing-action pattern (see the inputfield showcase page for a full example built ongogInputAddonEnd).AGENTS.md. A consumer-facing reference for AI coding agents building apps against the published package — conventions, theming/GOG_CONFIGsummary, a full per-component API table (inputs, outputs, slots, CVA status), and the deprecated-pattern list, all derived from the library's actual source rather than the (currently lagging)README.md. Shipped alongsideREADME.md/LICENSEin the npm package viang-package.json'sassets.
Fixed
gog-scroll: thumb too small to reliably click, especially atsize="thin". The thumb's own visible box is exactly as wide assizesays — that part is unchanged — but its clickable/draggable region now extends a few pixels past every edge (--gog-scroll-thumb-hit-padding, bigger onthin, where the visible thumb was hardest to land a cursor on), so a near-miss click still grabs the thumb instead of falling through to the track, which pages the view rather than dragging. Purely an invisible hit-area change — no new input, no behaviour change for the mouse wheel, which already worked fine.gog-autocomplete: option rows spilling out of the panel. The panel's.gog-scrollwas never actually constrained to--gog-autocomplete-panel-max-height— a classic flexbox trap where amax-height-only container doesn't give its flex-grow children a definite size to shrink into, so the option list rendered at full content height and visibly overflowed past the panel's own border into whatever sat below it. Most visible withappendToBodyand a longer list (typing narrowed it back under the cap, masking the issue until the panel was reopened with more matches, which also made it look like "the panel closes on its own" — it hadn't; the list had just spilled out from under it). Fixed by giving the panel the samedisplay: flex+flex: 1; min-height: 0chaingog-selectandgog-multiselectalready use, plus a defensiveoverflow: hidden.
08.08.2026
Added
Eight new components, the Angular Material set this library was missing:
gog-datepicker— a date field with a calendar panel: single date,selectionMode="range"(withnumberOfMonthsfor a two-month view), and an optional clock viashowTime/hourFormat/minuteStep/showSeconds.min,maxand adisabledDatespredicate (an array cannot express "weekends"),inlinefor an always-visible calendar,allowTextInputwith parsing, plus the usualclearable/floatLabel/errorMessage/appendToBody.The panel's footer carries two separate actions, never one:
showTodayButton(on by default) selects today, andshowThisMonthButton(off by default) only moves the view back to the current month. A single button doing both is ambiguous — after paging away, the same label reads as "take me back" to one person and "set it to today" to another. "Today" is disabled whenmin/maxordisabledDatesrule today out, rather than silently doing nothing. Wording viatodayLabel/thisMonthLabel.Native
Date, no date library and no adapter abstraction — the package keeps its zero runtime dependencies.Intlsupplies month and weekday names; the display format is a token pattern (dd.MM.yyyy,yyyy-MM-dd, …) used for both rendering and parsing, so what is written can always be read back.31.02.2026is rejected rather than silently becoming 3 March.locale,firstDayOfWeekandformatare also settable app-wide throughGOG_CONFIG.datepicker.gog-calendar— the month grid behind it, exported and usable on its own. Follows the ARIA grid pattern: arrows by day,PageUp/PageDownby month,Shift+ those by year,Home/Endto the week's ends, and one tab stop across all 42 cells. Always six weeks, so the calendar's height never changes as you page through months.gog-autocomplete— a text field that suggests options as you type, on the sameGogDropdownBaseasgog-selectand taking the sameoptionLabel/optionValue/optionDisabledaccessors. The trigger is a real<input>, which is what makes it a separate control rather than a mode ofgog-select: focus never leaves the field and the highlighted row is pointed at witharia-activedescendant.gogSearchis debounced (searchDebounce, 300 ms) for a server-backed source, and[filterLocal]="false"stops that server's answer being filtered a second time. PlusminLength,loading,emptyMessageandforceSelection.gog-tabs/gog-tab— a tablist over projected children, each tab declaring its ownlabel,iconNameanddisabled. Content written inside a tab renders eagerly and is merely hidden while inactive, so scroll position and half-typed input survive a switch; an<ng-template gogTabContent>is instead built on first activation and kept alive after. Which you get is decided by whether that template is present.gogTabHeaderreplaces the header button entirely. Overflowing headers scroll inside a<gog-scroll>, not a nativeoverflow-x.gog-button-toggle-group— a row of buttons where one, or withmultipleseveral, can be picked. Options-driven with the same accessors as the dropdowns, plusoptionIconand agogButtonToggleOptionslot. Single and multiple are genuinely different widgets to assistive tech and are exposed as such:role="radiogroup"/aria-checkedwith arrows that move and select, versusrole="group"/aria-pressedwith arrows that only move.appearancepicks between one segmented control and discrete buttons.gog-toggle— an on/off switch. A native<input type="checkbox">carryingrole="switch", so it announces as "switch, on" rather than "checkbox, checked" while the platform keeps owning the keyboard and forms.onLabel/offLabelrender inside the track — the one thing a checkbox cannot do — and both stay in the DOM so the track's width cannot jump as it flips. Sharesgog-checkbox's size scale.gog-progressbar— determinate, indeterminate and buffer modes, five sizes and the semantic colour set.valueandbufferare clamped to 0–100 rather than trusted. Indeterminate reports noaria-valuenowat all, which is what marks it indeterminate, and its animation is replaced by a static stripe underprefers-reduced-motion.gogBadge— a count or dot pinned to another element's corner. A directive, so it decorates a button, icon or avatar without wrapping it.badgePosition,badgeVariant,badgeDot,badgeMax(99+beyond it),badgeHiddenandbadgeAriaLabel. It renders nothing at all for0,nullor''— a badge reading "0" is the defining bug of this component class, so it is not reachable.gog-divider— a rule between two regions, horizontal or vertical, solid/dashed/dotted, with an optional projected label running through it and aninsetvariant for lists. NohasLabelinput: the two forms are told apart by whether anything was actually projected.
GogOrientation— one shared'horizontal' | 'vertical'type.GogSliderOrientationis now an alias of it, so nothing changes for existing code.roving-focus.tsgained anorientation(so a horizontal tablist leavesArrowDownto the page) and an optional predicate for skipping disabled items. Both default to the previous behaviour, sogog-select,gog-multiselectandgog-accordionare unaffected.Four icons:
calendar,clock,chevron-left,chevron-right..gog-visually-hiddeninstyles/utilities.css.GogFloatLabelState(exported) — the shared float-label state behindgog-inputfield,gog-textarea,gog-selectandgog-multiselect, previously three near-identical copies of the same fivecomputed()s. A plain composition class in the mould ofGogErrorState, so it serves the two components that share no base class as well asGogDropdownBase, which is one. Each control still supplies its own "has content" signal, since that genuinely differs (non-empty string / non-null selection / non-empty selection array).resolveConfigured(instanceValue, configuredValue, fallback)(exported) — the library's input →GOG_CONFIG→ built-in default precedence rule in one place, instead of the??chain hand-written at each configurable input.GOG_CONFIGnow covers the settings an app otherwise repeats on every instance:control.sizeandcontrol.errorDisplay(the latter is what makeserrorDisplay="auto"an app-wide decision for a Reactive Forms app rather than per-field boilerplate),dropdown.appendToBody,dropdown.direction, andtoast.position/toast.duration.control.sizedeliberately covers only the interactive form controls —gog-table,gog-accordionandgog-paginatorkeep their own density defaults, as dogog-spinner,gog-skeleton,gog-tagandgog-chip. All stay per-instance overridable.A built-in clear button on
gog-inputfield,gog-textarea,gog-selectandgog-multiselect, via aclearableinput (plusclearAriaLabel). It appears only once the control has something to clear and disappears again when empty, so it adds no permanent chrome — and it removes the need for a fake"— not selected —"option just to let someone undo a choice. Also settable app-wide throughGOG_CONFIG.control.clearable. Defaults tofalse, exceptgog-multiselect, which already had a clear button and keeps it. On a password field the built-in reveal toggle keeps the trailing slot.filterPositionongog-select/gog-multiselect('top'|'bottom', plusGOG_CONFIG.dropdown.filterPosition) sticks the search box to either end of the panel, and it now carries a divider on the side facing the list so it reads as chrome rather than a row. The name matchesgog-multiselect's existingcontrolsPositionrather than inventing a second vocabulary for the same idea.Filtering in
gog-selectandgog-multiselect—filterputs a search box at the top of the panel, matching case-insensitively on the resolvedoptionLabel.filterMatchswaps that for your own predicate,filterPlaceholderandfilterEmptyMessagecover the wording, andGOG_CONFIG.dropdown.filterturns it on app-wide. The query resets when the panel closes, andgog-multiselect's "select all" deliberately takes only the visible options so it means what it says while a filter is active.styles/presets/one-dark.cssandstyles/presets/one-light.css— the Atom/JetBrains One palettes, with the syntax hues mapped onto the library's semantic roles (blue is the accent, green/red/yellow/cyan become success/danger/warning/info).The token catalogue is generated, not hand-copied.
npm run generate:tokensderivesGogTokenName(a union of every--gog-*the library declares or documents), theGOG_TOKEN_GROUPSruntime metadata, and the README's theming table straight fromtheme.css.npm run check:tokensfails when they are out of date, so a stylesheet edit cannot silently leave the docs behind.GOG_TOKEN_GROUPSis exported so a theme editor can enumerate real tokens instead of keeping its own copy.styles/presets/slate.css— a second, importable preset (data-theme="slate", cool/indigo). It declares palette tokens only and still restyles everything, which is the theming contract demonstrated rather than described.gog-selectandgog-multiselecttake your own objects.optionLabel,optionValueandoptionDisabledaccept a property path (dot-paths included,'profile.fullName') or a function, so a real DTO goes straight in — no mapping into{ id, name }first, and no losing the original object on the way back out. Set[optionValue]="null"and the control emits the option object itself instead of an id. Both controls are now generic over their option and value types, inferred from the bindings.Defaults are
'name'/'id'/'disabled', so existing code is unaffected — the whole 21.2.x select/multiselect spec suite passes unchanged.GogDropdownOptionis no longer a requirement, just the shape those default accessors expect.gogDropdownOption— a projected template for one option row, with{ $implicit: option, selected, disabled, label }as its context.getByPath,readOption,isSameOptionValueand theGogOptionAccessor<TOption, TResult>type are exported;gog-tablenow shares the samegetByPathrather than keeping its own copy.One slot mechanism across the library. Custom markup is now projected as content and picked up with
contentChild, instead of aTemplateRefinput per slot. New directives:gogColumnBody/gogColumnHeader(per column, replacing the string-keyed<ng-template template="…" type="…">),gogCheckboxIcon,gogTagIcon,gogMultiselectClearIcon,gogDropdownChevron, andgogInputAddonStart/gogInputAddonEnd. A projected slot always wins over the deprecated input it replaces, so a codebase can migrate one call site at a time.GogColumnwith thegog-columnselector — the library's last unprefixed element name.gog-inputfieldaddon slots take arbitrary markup, including a real<button>with its ownaria-labeland(click). This replaces six inputs (icon{Start,End}{Template,Fn,Label}) with two slots. Ontype="password"the built-in reveal toggle keeps the trailing slot, so a projected addon can never displace the only control that shows the value.
Fixed
- An auto-width dropdown clipped its own options. With
[fullWidth]="false"the trigger sizes to the current selection, and the panel copied that width — so picking a short option cut the longer ones off the list. The relationship is now inverted: the panel sizes to its own content with the trigger's width as a floor, capped by--gog-{select,multiselect}-panel-max-width. NewminWidthinput (any CSS length) plus--gog-{select,multiselect}-min-width(120px) so an auto-width trigger cannot collapse to its own chrome either. gog-multiselectnow collapses a long selection into+N. The trigger shows what fits on one line and a count for the rest, with the full list in a tooltip. Measured withcanvas.measureTextrather than by rendering candidates, and re-measured from aResizeObserveron the value element, since the space available changes when the container resizes — something Angular never renders for.gog-select's chevron sat 42px from the trigger's right edge.--gog-select-chevron-insetwas applied as the trigger'spadding-rightwhile the chevron itself was a flex child inside that padding, so the inset was counted twice. It now lands on--gog-control-icon-offset(10px), the same line asgog-inputfield's icons andgog-multiselect's arrow, which were at 10px and 16px — the three controls did not line up in a form. The token keeps its name and now means what it says.gog-textarea's clear button sat inside the scrollbar. It was inset 8px from the border box while a scrolling textarea's scrollbar is ~19px wide, so once the content overflowed the button was half-covered and competed with the thumb for clicks. It is now offset by the measured scrollbar width (--gog-textarea-scrollbar-width, written fromoffsetWidth - clientWidth;scrollbar-gutter: stablewas rejected because it reserves the gutter even when the field isn't scrolling).gog-textarea's clear glyph was 30% too small — 13.4px against the library's 19.2px, because it reused the dropdowns' 0.7 ratio, which suits their dense trigger and not a large multi-line box. New--gog-textarea-clear-icon-ratiodefaults to a full-size glyph.- Nine specs in
scroll.component.spec.tsawaited a single animation frame after dispatching a scroll, whileScrollComponentcoalesces measurement into its own frame — if that frame fired duringwhenStable(), the effect scheduled a second one after the test's, and the assertion ran before the measurement. Intermittent by construction; replaced with asettleMeasure()helper that covers both orderings.
Changed
- The clear button now takes the outermost trailing position on
gog-selectandgog-multiselect, with the chevron/arrow shifting inward when it appears. Previouslygog-multiselecthad them the other way round. Keeps the trigger width stable and keeps the destructive control off the very edge. - Float-label fields are less tall:
--gog-field-float-label-reserve18px → 14px and--gog-field-float-label-in-top8px → 6px, taking anmdfield from 63px to 59px (a plain one is 45px). Both are tokens, so the old numbers are one declaration away.
Deprecated
Each of these keeps working unchanged and is removed in 21.5.0; the @deprecated tag on
every symbol carries the same date and removal version, so grep -rn "@deprecated since" lists
the full set at any time.
<column>→<gog-column>, and theColumnexport →GogColumn.- All
--gog-ms-*tokens →--gog-multiselect-*. Both spellings work for the whole window: the--gog-ms-*name stays the declared one and the new name derives from it, so an existing override of either still reaches the component. Verified in a browser both ways. <ng-template template="field" type="body|header">insidegog-table→ agogColumnBody/gogColumnHeadertemplate declared inside the column itself. The old form matched columns by a string the compiler cannot check, so a typo silently fell back to the default cell.gog-checkbox'scheckIconTemplate→gogCheckboxIcon.gog-tag'siconTemplate→gogTagIcon.gog-multiselect'sclearIconTemplate→gogMultiselectClearIcon.gog-select/gog-multiselectchevronTemplate→gogDropdownChevron.gog-inputfield'siconStartTemplate,iconEndTemplate,iconStartFn,iconEndFn,iconStartLabel,iconEndLabel→gogInputAddonStart/gogInputAddonEnd.iconStartandiconEnd(a bare icon name) stay — that is the genuinely common case.
Changed
provideGogConfig(...)now merges with the config from the parent injector instead of replacing it. Previously a nested call — in a route's or a component'sproviders— silently dropped every key it did not restate, so a route setting only{ tooltip: … }lost the app-widebutton.debouncewith no error anywhere. Merging is one level deep, per component key, nearest provider winning field by field. If you were working around the old behaviour by repeating the whole config at each level, those repeats are now redundant but harmless.Float label geometry is now themeable through
theme.cssinstead of being hardcoded in the component stylesheets.--gog-{input,select,ms}-float-label-{reserve,in-top,over-gap,over-reserve}previously existed only as literal fallbacks (18px,8px,1.4em) inside four component.scssfiles, so they were overridable but not discoverable, andtheme.cssdid not describe the components' full surface. They are now declared component tokens deriving from a new shared--gog-field-float-label-{reserve,in-top,over-gap,over-reserve}scale, so one declaration retunes every field at once while a single control can still be overridden. No visual change — the defaults are identical.--gog-{input,select,ms}-float-label-on-bgstays an instance-layer (undeclared) token as before.
Added
- Float label support for
gog-inputfield,gog-select,gog-multiselectandgog-textarea: afloatLabelinput (GogFloatLabelVariant:'none'default, or'in'/'on'/'over', modeled on PrimeNG's own variant names) that rests the label inside the field like a placeholder and floats it up on focus or once the field has content —'in'stays fully inside the border,'on'ends up centered on the top border line (with a background patch masking it),'over'floats fully above the field, outside the border. AfloatLabelShowPlaceholderinput (boolean, defaultfalse) reveals the field's ownplaceholderonce the label has floated out of the way; left off, the placeholder stays hidden the whole time a float label is active since the resting label already occupies that space. Both are also settable app-wide via the newGOG_CONFIG.floatLabel(variant/showPlaceholder), with the usual per-instance input taking priority. Implemented as a style variant on each component (not a directive, unlikegogTooltip) since each control already owns its label and has a different notion of "has content" (value,selectedOption, selection length) that a directive sitting outside the component couldn't see. New--gog-{input,select,ms}-float-label-{in-top,on-bg,over-gap,over-reserve}tokens. gog-slider's neworientationinput ('horizontal'default /'vertical') — the developer picks per instance, no global default, since it's a layout decision rather than a house style. The vertical variant is the same native<input type="range">rotated viawriting-mode: vertical-lr+direction: rtl(not a custom drag implementation), so dragging, touch and keyboard (Up/Down as well as Left/Right) all keep working exactly as they do horizontally; value increases upward, matching a volume-fader convention. New--gog-slider-vertical-lengthtoken (default160px) sizes its length, the vertical counterpart to--gog-slider-auto-width.fullWidthis ignored when vertical, since a vertical slider's width is its thickness, not its length.gog-radio-group: a new options-driven radio control (GogRadioOption[]), the radio counterpart togog-checkbox. Renders native<input type="radio">s sharing one auto-generated (or explicitname) group name, so mutual exclusivity and arrow-key/Home/End navigation between options come from the browser for free — no roving-focus code needed.ControlValueAccessor-based, works withformControl/formControlName.label,ariaLabel,name,size,disabled(group-level, plus per-optiondisabled),orientation('vertical'default /'horizontal'),errorMessage,errorDisplayandfullWidthinputs;[(value)]two-way bindable. Reuses the--gog-control-checkbox-*size scale via the shared checkable-control config, plus new--gog-radio-*tokens intheme.css.gog-collapsible'scollapseOnFocusOutinput (boolean, defaultfalse): closes the panel once focus leaves both the trigger and the content — e.g. Tabbing past the last focusable element inside, or a click landing elsewhere on the page. Off by default, since plenty of consumers (an FAQ list, a settings section read top to bottom) want the panel to stay open regardless of where focus goes next.gogTooltip: a new directive, not a component — drop it on any element, agog-*component's own host tag or a plain native one (<button gogTooltip="Save changes">,<gog-chip [gogTooltip]="hint">), to add a hover/focus tooltip without that element needing to know anything about it. Content is a plain string or aTemplateReffor richer markup.gogTooltipPosition(GogTooltipPosition:'auto'default, or an explicit'top'/'bottom'/'left'/'right'that flips to its opposite if it has no room),gogTooltipShowDelay(default300ms),gogTooltipHideDelay(default100ms) andgogTooltipDisabledinputs; the first three also readGOG_CONFIG.tooltipfor an app-wide default the same waygog-scroll/gog-buttonalready do, with an instance's own input always winning. Shown on both mouse hover and keyboard focus (focusin/focusout, notfocus/blur, so it stays replay-safe under SSR event replay), dismissible with Escape, and hoverable — moving the pointer from the trigger onto the bubble itself (e.g. to read more of a long one, or scroll one taller than--gog-tooltip-max-height) cancels the pending hide instead of racing it — per WCAG 2.1 SC 1.4.13. The bubble is appended todocument.body(so it's never clipped by an ancestor'soverflow: hidden) via a new internalGogTooltipOverlay, built onViewContainerRef.createComponent+ relocating the node rather thanGogDropdownOverlay'sTemplateRefapproach, since a directive has no template of its own to attach from. Visually it's the same "floating panel" recipe asgog-dialog's panel andgog-select's dropdown (--gog-surface-colorbackground, plain--gog-border-colorborder,--gog-panel-shadow), not a bespoke inverted bubble, so it reads as part of a themed app rather than a generic dark tooltip dropped on top of it. Content wraps to--gog-tooltip-max-width(280px) and is capped at--gog-tooltip-max-height(220px) through an internalgog-scroll— content under the cap renders at exactly its own height, content over it scrolls, using the same themeable scrollbar every other overflowing panel in this library uses instead of a native one (seestyling.instructions.md's new "Scrollable content" section for that convention).gogTooltipClassapplies a class straight to the bubble, for restyling (or resizing) one instance — needed because the bubble sits outside any scoped ancestor's stylesheet once appended todocument.body, the same "Panels rendered outside the component subtree" limitationgog-select's[appendToBody]panel already has, so the class has to come from an unscoped (global) stylesheet. New--gog-tooltip-*tokens intheme.css;gog-dialog's panel now also raises--gog-tooltip-z(mirroring the existing--gog-dropdown-zbump) so a tooltip triggered inside a dialog stacks above it.
Changed
gog-slider's track now paints a border (new--gog-slider-track-border-width/-style/-colortokens, transparent by default — same opt-in convention as--gog-btn-primary-border) and its fill is bound viabackgroundinstead ofbackground-color, so--gog-slider-fill-bgalso accepts a gradient (e.g.linear-gradient(...)), not just a solid color. The thumb ("handle") was already fully customizable via its existing--gog-slider-thumb-*tokens (size, background, border, radius, glow) — no change there.
Fixed
gog-slider's track background (--gog-slider-track-bg) no longer reuses--gog-accent-dim— it sat on the same hue ramp as the fill (--gog-accent-color), so at the track's 4px height the two read as one blob instead of a recessed groove with an accent fill on top. Nowcolor-mix(in srgb, var(--gog-text-color) 30%, var(--gog-border-color)): a desaturated, theme-adaptive gray that darkens toward black in the light theme and lightens toward parchment in the dark theme (--gog-text-colorsits at whichever end of that range per theme), so it's always distinct from the accent-colored fill and legible against its own theme's surface.
05.08.2026
Added
gog-collapsible: a headless expand/collapse primitive — inline, not a portal (unlikegog-select/gog-multiselect's panel). Owns no markup: project any element as the trigger viagogCollapsibleTriggerand any element as the panel viagogCollapsibleContent;[(open)]is two-way bindable,disabledblocks toggling. New--gog-collapsible-*tokens intheme.css; the trigger/content CSS classes live inutilities.csssince the projected content sits outside the component's own view.gog-textarea: a multi-line counterpart togog-inputfield, sharing its--gog-input-*tokens.ControlValueAccessor-based, works withformControl/formControlName.label,placeholder,errorMessage,errorDisplay,disabled,size,fullWidthandrowsinputs.gog-inputfield'stypeinput now also accepts'number'and'date', plus newmin/max/stepinputs (applied only fortype="number"). For anumberfield the value written to/read from an attachedformControl/formControlNameis anumber(nullwhen the field is empty) rather than a string —[(value)]stays a string either way, since it mirrors the native input's raw text.gog-scroll: a drop-in replacement for a nativeoverflow: autoregion. Content keeps scrolling natively (wheel, touch, keyboard, focus-into-view); only the browser's own scrollbar chrome is hidden and replaced with a themeable, draggable overlay thumb.axis(vertical/horizontal/both),size(normal/thin),autoHide/hideDelay,reachThresholdwithgogReachStart/gogReachEndoutputs, agogScrollmetrics output, andscrollTo/scrollToTop/scrollToBottom/scrollToLeft/scrollToRightpublic methods. New--gog-scroll-*tokens intheme.css.gog-scroll'soverscrollBehaviorinput ('auto'|'contain'|'none', mirrors the CSS property of the same name): what happens when a scroll gesture reaches this instance's edge. Defaults to'auto'— chains to the next scrollable ancestor, same as an un-customizedoverflow: autodiv, so scrolling to the end of agog-scroll'd section and continuing the same gesture now keeps scrolling the page instead of stopping dead.gog-select/gog-multiselect's option panel andgog-dialog's body now setoverscrollBehavior="contain"explicitly, preserving their existing (correct, overlay-appropriate) behavior now that the component-wide default has changed to chain-through.GOG_CONFIG/GogGlobalConfig/provideGogConfig(...): one injection token for app-wide defaults across the library's component inputs, instead of a separate token per component per setting. CallprovideGogConfig({ scroll: {...}, button: {...} })once in your app's providers (or a route's/ component's ownprovidersfor a subtree-scoped override); any instance that doesn't set the input itself falls back to the configured value, then to the component's own hardcoded default.gog-scroll'ssize,autoHide,hideDelayandoverscrollBehaviorandgog-button'sdebounceare the first inputs wired up to it — see the "Global configuration" section ingleks-ui-library.instructions.mdfor how to add more. This only covers inputs read in TypeScript that can't already be a CSS token; visual defaults remain the--gog-*custom properties intheme.css.
Changed
gog-checkboxnow registers itsControlValueAccessorby self-injectingNgControlin the constructor, matching every other form control in the library, instead of theNG_VALUE_ACCESSOR/forwardRefprovider pattern. No behavior change —formControl/formControlNameusage is unaffected.gog-scroll'ssize,autoHide,hideDelayandoverscrollBehaviorinputs andgog-button'sdebounceinput now default toundefinedinstead of a hardcoded value, so they can fall through toGOG_CONFIG— read the resolved value (e.g. via the rendered DOM) rather than the raw input signal if you need the effective default.gog-selectandgog-multiselect: the option panel now scrolls viagog-scrollinstead of nativeoverflow-y.gog-dialog: the body now scrolls viagog-scrollinstead of nativeoverflow-y.gog-table: horizontal scrolling now goes throughgog-scrollinstead of nativeoverflow-x.
Fixed
gog-scrollinternals usedheight: 100%chains from:hostdown to the viewport. A host whose own height comes from being flex-grown inside amax-height-only ancestor (exactly the select/multiselect dropdown panel and dialog body cases above) still failed to resolve a percentage height read off it, collapsing back to content size — the panel stopped clipping and scrolling. Switched every level to flex-basis chains (flex: 1 1 auto+min-height: 0), which don't have that failure mode.gog-scroll's horizontal content wrapper usedwidth: max-content, which created a circular sizing reference against awidth: 100%child (e.g.gog-table's own<table>) and made some browsers fall back to a huge sentinel width (~1,000,000px), pushing the table off-screen. Removed — children already overflow a normal block parent without it.gog-scrollsetoverscroll-behavior: contain(both axes) on the viewport unconditionally, which also blocked wheel scroll on an axis the instance never actually scrolls (e.g. vertical wheel over a horizontal-only instance), preventing it from bubbling up to scroll the page. Now set only on the axis that's actually acting as a scroll container.gog-scrollkept a disabled or currently-non-overflowing axis atoverflow: hidden/auto, which makes an element a "scroll container" per spec regardless of whether it has anything to scroll — becoming the containing block forposition: stickydescendants and a scroll-chaining boundary, whether needed or not. This brokegog-table'sstickyHeaderand swallowed wheel scroll whenever agog-table(which always wraps its own horizontal scroll in agog-scroll) was itself nested inside another scrolling container, e.g. agog-scrollcapping its height. Both axes are nowvisibleunless that specific axis is genuinely scrolling.
03.08.2026
Added
fullWidthinput ongog-checkbox,gog-chipandgog-tag, matching the existinggog-buttonbehavior:falseby default (sized to content),truestretches the component to fill its container.fullWidthinput ongog-inputfield,gog-select,gog-multiselect,gog-table,gog-paginatorandgog-slider. Inverted from the input above: these are already full width of their container by default, sofullWidthdefaults totrueand set it tofalseto shrink the control to fit its content instead (a fixed--gog-slider-auto-width, 240px by default, forgog-sliderspecifically — its track has no content of its own to size to).gog-accordion'sskeletonCountinput: how many skeleton rows to render whileloadingis true anditemsis still empty. Defaults to3.
Changed
gog-accordion'sloadingskeleton now renders withgog-skeletoninstead of a bespoke shimmer implementation. Breaking: the--gog-accordion-skeleton-start/-mid/-end/-radius/-height/-width/-durationtokens are gone — restyle the loading state via the shared--gog-skeleton-*tokens instead.
Fixed
gog-accordion'sloadingskeleton now actually renders whileitemsis empty. It previously rendered one skeleton row per existing item, so the most common real-world case — showing loading state before the item list has arrived at all — silently rendered nothing. It now falls back toskeletonCountrows wheneveritemsis empty, and still mirrorsitemsonce they exist.gog-accordion's chevron no longer force-rotates 180° when a customgogAccordionChevrontemplate is supplied. Previously the wrapper always rotated on open regardless of what the template rendered, so a template that swapped between achevron-up/chevron-downicon peropenstate ended up double-transformed (both states visually pointing the same way). The rotation now only applies to the built-in default chevron; a custom template owns its open/closed presentation entirely, including bringing its own animation or swapping in a completely different icon.
30.07.2026
Added
column'scomparatorinput for custom per-column sort ordering; the default comparator now usesIntl.Collatorfor numeric-aware string sorting ("item2" < "item10") instead of raw</>.gog-tablecell/sort values now resolve dot-path nested fields (e.g.field="address.city").- ESLint (
@angular-eslint, flat config) across@gleks/uiandui-showcase, wired into CI alongsideformat:checkand a token-consistency check (everyvar(--gog-*)read with no fallback must resolve to a declared default). LICENSE(MIT) and this changelog.
Changed
- Breaking: every previously unprefixed global design token in
theme.css(--accent-color,--text-color,--radius,--control-*,--field-*,--dropdown-z, etc.) is now--gog-*prefixed, matching the component-token convention. Update any consumer theme overrides to the new names. - Breaking:
column'sfieldinput is now a plainstring(waskeyof T & string) to support nested dot-paths. gog-select/gog-multiselectpanel sizing constants (max height, estimated row height) are now read from CSS custom properties (--gog-select-panel-max-height,--gog-select-option-height, and the multiselect equivalents) instead of hardcoded in TypeScript, so they're themeable.gog-table's pagination state now useslinkedSignalinstead of a manualeffect, resetting to page 1 on sort changes and clamping tototalPageson data/page-size changes, while still deferring togog-paginator's own self-clampingpagemodel.
Fixed
- An append-to-body dropdown panel now copies the trigger's scoped
data-theme(not just:root's) onto its overlay host, so panels stay themed when opened inside a themed subtree. gog-selectnow correctly reads its own--gog-select-option-gaptoken for panel-height estimation instead of the unused base default, fixing a latent under-estimate in the panel's up/down placement math.
Initial development, published as 0.0.1: accordion, button, checkbox, chip, dialog,
icon, inputfield, multiselect, paginator, select, skeleton, slider, spinner, table, tag
and toast components, plus the shared theme (styles/theme.css) and ThemeService.
Versions up to 0.2.2 were developed without per-release changelog entries. 0.2.2 was
published with the wrong version scheme and immediately re-published, with no code
changes, as 21.2.2 — this file tracks changes from 21.2.2 onward.