twico-ui 1.47.0 → 1.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  All notable changes to Twico UI are documented here (newest first). This file is generated automatically on each release by [semantic-release](https://semantic-release.gitbook.io/) from [Conventional Commits](https://www.conventionalcommits.org/) — do not edit it by hand. The same notes appear on each [GitHub Release](https://github.com/EncryptionL/twico-ui/releases).
4
4
 
5
+ # [1.49.0](https://github.com/EncryptionL/twico-ui/compare/v1.48.0...v1.49.0) (2026-11-05)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * **tokens:** make the plain tones clear 4.5:1 against their own ink ([78a727d](https://github.com/EncryptionL/twico-ui/commit/78a727d193010a717405ee14af038e39f1baf65b)), closes [#455](https://github.com/EncryptionL/twico-ui/issues/455) [#504](https://github.com/EncryptionL/twico-ui/issues/504) [#504](https://github.com/EncryptionL/twico-ui/issues/504) [#1e1b4b](https://github.com/EncryptionL/twico-ui/issues/1e1b4b) [#e11d48](https://github.com/EncryptionL/twico-ui/issues/e11d48) [#1e1b4b](https://github.com/EncryptionL/twico-ui/issues/1e1b4b) [#818cf8](https://github.com/EncryptionL/twico-ui/issues/818cf8) [#052e1d](https://github.com/EncryptionL/twico-ui/issues/052e1d) [#10b981](https://github.com/EncryptionL/twico-ui/issues/10b981) [#2c0610](https://github.com/EncryptionL/twico-ui/issues/2c0610) [#f43f5e](https://github.com/EncryptionL/twico-ui/issues/f43f5e)
11
+
12
+
13
+ ### Features
14
+
15
+ * **button:** widen tone to the full Tone scale, not just primary|danger ([ec640c3](https://github.com/EncryptionL/twico-ui/commit/ec640c3f4c0faa5c19b6cab4b73a8a5758628864)), closes [#451a03](https://github.com/EncryptionL/twico-ui/issues/451a03) [#082f49](https://github.com/EncryptionL/twico-ui/issues/082f49)
16
+
17
+ # [1.48.0](https://github.com/EncryptionL/twico-ui/compare/v1.47.0...v1.48.0) (2026-10-23)
18
+
19
+
20
+ ### Features
21
+
22
+ * **datatable:** vertical alignment at three levels — table, row, column ([221978b](https://github.com/EncryptionL/twico-ui/commit/221978b1db3527d97b93335a9b36b9274d2c8b10))
23
+
5
24
  # [1.47.0](https://github.com/EncryptionL/twico-ui/compare/v1.46.0...v1.47.0) (2026-10-23)
6
25
 
7
26
 
package/dist/icons.d.cts CHANGED
@@ -56,4 +56,5 @@ declare const brandIcons: BrandIconMeta[];
56
56
  /** Just the exported component names of every brand icon. */
57
57
  declare const brandIconNames: string[];
58
58
 
59
- export { AppleBrandIcon, BlueskyIcon, type BrandIcon, type BrandIconMeta, type BrandIconProps, DiscordIcon, DockerIcon, DribbbleIcon, FacebookIcon, FigmaIcon, GithubIcon, GitlabIcon, GoogleIcon, InstagramIcon, MastodonIcon, MediumIcon, NextjsIcon, NodejsIcon, NpmIcon, PinterestIcon, ReactjsIcon, RedditIcon, SpotifyIcon, StackOverflowIcon, TailwindIcon, TelegramIcon, ThreadsIcon, TikTokIcon, TwitchIcon, TypeScriptIcon, VercelIcon, WhatsAppIcon, XTwitterIcon, YoutubeIcon, brandIconNames, brandIcons };
59
+ export { AppleBrandIcon, BlueskyIcon, DiscordIcon, DockerIcon, DribbbleIcon, FacebookIcon, FigmaIcon, GithubIcon, GitlabIcon, GoogleIcon, InstagramIcon, MastodonIcon, MediumIcon, NextjsIcon, NodejsIcon, NpmIcon, PinterestIcon, ReactjsIcon, RedditIcon, SpotifyIcon, StackOverflowIcon, TailwindIcon, TelegramIcon, ThreadsIcon, TikTokIcon, TwitchIcon, TypeScriptIcon, VercelIcon, WhatsAppIcon, XTwitterIcon, YoutubeIcon, brandIconNames, brandIcons };
60
+ export type { BrandIcon, BrandIconMeta, BrandIconProps };
package/dist/icons.d.ts CHANGED
@@ -56,4 +56,5 @@ declare const brandIcons: BrandIconMeta[];
56
56
  /** Just the exported component names of every brand icon. */
57
57
  declare const brandIconNames: string[];
58
58
 
59
- export { AppleBrandIcon, BlueskyIcon, type BrandIcon, type BrandIconMeta, type BrandIconProps, DiscordIcon, DockerIcon, DribbbleIcon, FacebookIcon, FigmaIcon, GithubIcon, GitlabIcon, GoogleIcon, InstagramIcon, MastodonIcon, MediumIcon, NextjsIcon, NodejsIcon, NpmIcon, PinterestIcon, ReactjsIcon, RedditIcon, SpotifyIcon, StackOverflowIcon, TailwindIcon, TelegramIcon, ThreadsIcon, TikTokIcon, TwitchIcon, TypeScriptIcon, VercelIcon, WhatsAppIcon, XTwitterIcon, YoutubeIcon, brandIconNames, brandIcons };
59
+ export { AppleBrandIcon, BlueskyIcon, DiscordIcon, DockerIcon, DribbbleIcon, FacebookIcon, FigmaIcon, GithubIcon, GitlabIcon, GoogleIcon, InstagramIcon, MastodonIcon, MediumIcon, NextjsIcon, NodejsIcon, NpmIcon, PinterestIcon, ReactjsIcon, RedditIcon, SpotifyIcon, StackOverflowIcon, TailwindIcon, TelegramIcon, ThreadsIcon, TikTokIcon, TwitchIcon, TypeScriptIcon, VercelIcon, WhatsAppIcon, XTwitterIcon, YoutubeIcon, brandIconNames, brandIcons };
60
+ export type { BrandIcon, BrandIconMeta, BrandIconProps };
package/dist/index.cjs CHANGED
@@ -455,6 +455,22 @@ var ICONBTN_CSS = `
455
455
  --_accent-fg: var(--color-primary-fg); --_accent-subtle: var(--color-primary-subtle); --_accent-subtle-fg: var(--color-primary-subtle-fg); }
456
456
  .twc-iconbtn[data-tone="danger"] { --_accent: var(--color-danger); --_accent-hover: var(--color-danger-hover);
457
457
  --_accent-fg: var(--color-danger-fg); --_accent-subtle: var(--color-danger-subtle); --_accent-subtle-fg: var(--color-danger-subtle-fg); }
458
+ /* #504: the full Tone scale, not just primary|danger. A standalone affirmative action - a row-level
459
+ confirm sitting next to a danger sibling, reading as "yes / no" - could not be green, so the colour
460
+ that carried the distinction came out the same blue as every neutral action on the page.
461
+ success/warning/info now have real -hover/-active tokens (added in tokens/colors.css on the same
462
+ step rule as primary and danger: light goes darker, dark goes lighter), so hover always moves AWAY
463
+ from the ink and contrast only improves. neutral has no accent tokens anywhere and is mapped the way
464
+ Badge already maps it - --color-text on --color-surface solid, --color-surface-sunken soft - with its
465
+ hover mixed from currentColor, since there is no --color-text-hover to point at. */
466
+ .twc-iconbtn[data-tone="success"] { --_accent: var(--color-success); --_accent-hover: var(--color-success-hover);
467
+ --_accent-fg: var(--color-success-fg); --_accent-subtle: var(--color-success-subtle); --_accent-subtle-fg: var(--color-success-subtle-fg); }
468
+ .twc-iconbtn[data-tone="warning"] { --_accent: var(--color-warning); --_accent-hover: var(--color-warning-hover);
469
+ --_accent-fg: var(--color-warning-fg); --_accent-subtle: var(--color-warning-subtle); --_accent-subtle-fg: var(--color-warning-subtle-fg); }
470
+ .twc-iconbtn[data-tone="info"] { --_accent: var(--color-info); --_accent-hover: var(--color-info-hover);
471
+ --_accent-fg: var(--color-info-fg); --_accent-subtle: var(--color-info-subtle); --_accent-subtle-fg: var(--color-info-subtle-fg); }
472
+ .twc-iconbtn[data-tone="neutral"] { --_accent: var(--color-text); --_accent-hover: color-mix(in srgb, var(--color-text) 88%, var(--color-surface));
473
+ --_accent-fg: var(--color-surface); --_accent-subtle: var(--color-surface-sunken); --_accent-subtle-fg: var(--color-text-muted); }
458
474
  .twc-iconbtn[data-variant="solid"] { background: var(--_accent); color: var(--_accent-fg); }
459
475
  .twc-iconbtn[data-variant="solid"]:hover:not(:disabled) { background: var(--_accent-hover); box-shadow: var(--shadow-brand); }
460
476
  .twc-iconbtn[data-variant="solid"]:active:not(:disabled) { filter: brightness(0.92); }
@@ -1832,6 +1848,31 @@ var CSS2 = `
1832
1848
  --_accent: var(--color-danger); --_accent-hover: var(--color-danger-hover); --_accent-active: var(--color-danger-hover);
1833
1849
  --_accent-fg: var(--color-danger-fg); --_accent-subtle: var(--color-danger-subtle); --_accent-subtle-fg: var(--color-danger-subtle-fg);
1834
1850
  }
1851
+ /* #504: the full Tone scale, not just primary|danger. A standalone affirmative action - a row-level
1852
+ confirm sitting next to a danger sibling, reading as "yes / no" - could not be green, so the colour
1853
+ that carried the distinction came out the same blue as every neutral action on the page.
1854
+ success/warning/info now have real -hover/-active tokens (added in tokens/colors.css on the same
1855
+ step rule as primary and danger: light goes darker, dark goes lighter), so hover always moves AWAY
1856
+ from the ink and contrast only improves. neutral has no accent tokens anywhere and is mapped the way
1857
+ Badge already maps it - --color-text on --color-surface solid, --color-surface-sunken soft - with its
1858
+ hover mixed from currentColor, since there is no --color-text-hover to point at. */
1859
+ .twc-btn[data-tone="success"] {
1860
+ --_accent: var(--color-success); --_accent-hover: var(--color-success-hover); --_accent-active: var(--color-success-active);
1861
+ --_accent-fg: var(--color-success-fg); --_accent-subtle: var(--color-success-subtle); --_accent-subtle-fg: var(--color-success-subtle-fg);
1862
+ }
1863
+ .twc-btn[data-tone="warning"] {
1864
+ --_accent: var(--color-warning); --_accent-hover: var(--color-warning-hover); --_accent-active: var(--color-warning-active);
1865
+ --_accent-fg: var(--color-warning-fg); --_accent-subtle: var(--color-warning-subtle); --_accent-subtle-fg: var(--color-warning-subtle-fg);
1866
+ }
1867
+ .twc-btn[data-tone="info"] {
1868
+ --_accent: var(--color-info); --_accent-hover: var(--color-info-hover); --_accent-active: var(--color-info-active);
1869
+ --_accent-fg: var(--color-info-fg); --_accent-subtle: var(--color-info-subtle); --_accent-subtle-fg: var(--color-info-subtle-fg);
1870
+ }
1871
+ .twc-btn[data-tone="neutral"] {
1872
+ --_accent: var(--color-text); --_accent-hover: color-mix(in srgb, var(--color-text) 88%, var(--color-surface));
1873
+ --_accent-active: color-mix(in srgb, var(--color-text) 78%, var(--color-surface));
1874
+ --_accent-fg: var(--color-surface); --_accent-subtle: var(--color-surface-sunken); --_accent-subtle-fg: var(--color-text-muted);
1875
+ }
1835
1876
  /* solid */
1836
1877
  .twc-btn[data-variant="solid"] { background: var(--_accent); color: var(--_accent-fg); }
1837
1878
  .twc-btn[data-variant="solid"]:hover:not(:disabled) { background: var(--_accent-hover); box-shadow: var(--shadow-brand); }
@@ -9378,9 +9419,20 @@ var DT_CSS = `
9378
9419
  font-size: var(--text-sm); color: var(--color-text); background: var(--twc-dt-row-bg, var(--color-surface));
9379
9420
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 0; }
9380
9421
  /* Wrap-text columns: content flows onto multiple lines (the row grows down) instead of
9381
- clipping to one line. height stays a per-row minimum, so single-line cells are unchanged. */
9422
+ clipping to one line. height stays a per-row minimum, so single-line cells are unchanged.
9423
+ The top alignment here is only a DEFAULT for wrapped cells - an explicit verticalAlign at any
9424
+ level resolves to data-valign below, which is more specific and wins. Before that existed, a row
9425
+ mixing a wrapped column with normal ones was silently inconsistent: the wrapped cell sat top and
9426
+ its neighbours sat middle, with no way to say otherwise. */
9382
9427
  .twc-dt__td[data-wrap="true"] { white-space: normal; overflow: hidden; text-overflow: clip;
9383
9428
  word-break: break-word; overflow-wrap: anywhere; vertical-align: top; padding-block: 10px; line-height: 1.45; }
9429
+ /* Vertical alignment. One attribute, already resolved: column > row > table default, computed in
9430
+ renderLeaf rather than left to three competing selectors, so the precedence is readable in the
9431
+ code and cannot be reordered by an unrelated CSS edit. Matters whenever rows vary in height -
9432
+ wrapText, a multi-line renderCell, or a drag-resized row. */
9433
+ .twc-dt__td[data-valign="top"] { vertical-align: top; }
9434
+ .twc-dt__td[data-valign="middle"] { vertical-align: middle; }
9435
+ .twc-dt__td[data-valign="bottom"] { vertical-align: bottom; }
9384
9436
  /* #338: "combined" column cell \u2014 one column showing several source columns' values. */
9385
9437
  .twc-dt__combine--stack { display: flex; flex-direction: column; gap: 1px; }
9386
9438
  .twc-dt__combine-sep { color: var(--color-text-subtle); padding-inline: 3px; }
@@ -10149,6 +10201,8 @@ function Datatable({
10149
10201
  batchActions = [],
10150
10202
  onRowSelectionChange,
10151
10203
  selectionSummary,
10204
+ verticalAlign,
10205
+ getRowVerticalAlign,
10152
10206
  getRowStyle,
10153
10207
  getRowClassName,
10154
10208
  showExport = false,
@@ -12728,6 +12782,7 @@ function Datatable({
12728
12782
  const h = rowHeights[k];
12729
12783
  const userRowStyle = getRowStyle ? getRowStyle(row, ri) : void 0;
12730
12784
  const userRowClass = getRowClassName ? getRowClassName(row, ri) : void 0;
12785
+ const rowValign = getRowVerticalAlign ? getRowVerticalAlign(row, ri) : void 0;
12731
12786
  const { background: _rowBgShorthand, backgroundColor: _rowBgColor, ...restRowStyle } = userRowStyle || {};
12732
12787
  const usable = (v) => typeof v === "string" && v.trim() !== "" ? v : void 0;
12733
12788
  const rowBg = usable(_rowBgShorthand) ?? usable(_rowBgColor);
@@ -12838,6 +12893,7 @@ function Datatable({
12838
12893
  "data-num": c.type === "number" || void 0,
12839
12894
  "data-actions": isActions || void 0,
12840
12895
  "data-align": c.align !== dtNaturalAlign(c) ? c.align : void 0,
12896
+ "data-valign": c.verticalAlign ?? rowValign ?? verticalAlign,
12841
12897
  "data-editable": editable && !isEditing || void 0,
12842
12898
  "data-editing": isEditing || void 0,
12843
12899
  "data-cell-active": cellActive || void 0,