From 3139bfce572ae8a13ec7ac4f536925736412a2bf Mon Sep 17 00:00:00 2001 From: francisrupert Date: Mon, 2 Mar 2026 19:05:27 -0600 Subject: [PATCH 01/47] feat(tokens,css,link,text,breadcrumbs,input-group): NO-JIRA tokens, CSS foundation, deprecations, tooling, scratch page Dark theme tokens, radius-350, CSS :where() specificity softening, import ordering, watch tooling, inverted deprecations (link, text, breadcrumbs, keyboard-shortcut), input-group deprecation, deprecated badge in API table, scratch page. --- .claude/rules/css-specificity.md | 96 ++ .../theme/assets/less/dialtone-docs.less | 40 +- .../theme/assets/less/dialtone-syntax.less | 21 +- .../theme/components/SidebarItem.vue | 2 +- .../.vuepress/theme/components/TocItem.vue | 2 +- .../docs/.vuepress/views/Overview.vue | 46 +- .../docs/_data/site-nav.json | 8 +- .../docs/components/input-group.md | 2 +- .../docs/components/link.md | 57 +- .../docs/components/mode-island.md | 54 +- .../docs/components/stack.md | 2 +- .../docs/components/table.md | 136 ++- .../docs/components/text.md | 53 +- .../docs/guides/contributing/index.md | 6 + apps/dialtone-documentation/docs/scratch.md | 873 ++++++++++++++++++ .../docs/utilities/borders/radius.md | 5 +- apps/dialtone-documentation/project.json | 5 +- packages/dialtone-css/.github/CONTRIBUTING.md | 79 ++ packages/dialtone-css/gulpfile.cjs | 5 +- .../lib/build/less/components/card.less | 1 + .../lib/build/less/components/selects.less | 2 +- .../lib/build/less/components/table.less | 27 +- .../dialtone-css/lib/build/less/dialtone.less | 32 +- packages/dialtone-css/postcss/constants.cjs | 1 + packages/dialtone-css/project.json | 14 + .../dialtone-tokens/tokens/theme/dp/dark.json | 20 +- .../breadcrumbs/breadcrumb_item.stories.js | 3 + .../breadcrumbs/breadcrumbs.stories.js | 3 + .../breadcrumbs_variants.story.vue | 7 - .../components/card/card_default.story.vue | 22 +- .../filter_pill/filter_pill.stories.js | 2 +- .../components/hovercard/hovercard.stories.js | 15 +- .../components/icon/icon_variants.story.vue | 27 +- .../input_group/input_group.stories.js | 7 + .../input_group_variants.story.vue | 10 +- .../kitchen_sink/kitchen_sink_view.vue | 3 + .../components/link/link.stories.js | 3 +- .../dialtone-vue/components/link/link.vue | 1 + .../components/link/link_default.story.vue | 1 + .../components/link/link_variants.story.vue | 35 +- .../presence/presence_variants.story.vue | 51 +- .../components/root_layout/root_layout.vue | 9 +- .../scroller/scroller_default.story.vue | 2 +- .../scroller/scroller_dynamic.story.vue | 2 +- .../components/text/text_variants.story.vue | 20 +- .../feed_item_row_variants.story.vue | 2 +- 46 files changed, 1460 insertions(+), 354 deletions(-) create mode 100644 .claude/rules/css-specificity.md diff --git a/.claude/rules/css-specificity.md b/.claude/rules/css-specificity.md new file mode 100644 index 0000000000..b646b5a016 --- /dev/null +++ b/.claude/rules/css-specificity.md @@ -0,0 +1,96 @@ +--- +paths: + - "packages/dialtone-css/**" +--- + +# CSS Specificity Rules + +## Target: (0,1,0) Per Selector + +Every component selector should aim for a specificity of (0,1,0) — one class. BEM naturally achieves this: + +```less +.d-banner {} // (0,1,0) — block +.d-banner__dialog {} // (0,1,0) — element +.d-banner--success {} // (0,1,0) — modifier +``` + +Modifier overrides should use CSS custom properties, not higher specificity: + +```less +// CORRECT — modifier overrides a variable, same specificity +.d-banner { --banner-color: var(--dt-color-surface-primary); } +.d-banner--success { --banner-color: var(--dt-color-surface-success); } + +// AVOID — parent modifier targets child, inflates to (0,2,0) +.d-banner--success .d-banner__icon { color: green; } +``` + +## Element-Type Descendants: Always Wrap in `:where()` + +When a component must style bare HTML elements (`th`, `td`, `a`, `button`, `option`, `li`, etc.), wrap them in `:where()` to zero out the element's specificity contribution: + +```less +// CORRECT — (0,1,0) +.d-table { + :where(th) { color: var(--table-th-color-text); } + :where(td) { color: var(--table-td-color-text); } +} + +// WRONG — (0,1,1), harder to override +.d-table { + th { color: var(--table-th-color-text); } +} +``` + +## Structural Pseudo-Classes on Elements: Wrap the Full Descendant + +When using `:first-child`, `:last-of-type`, `:nth-child()`, etc. on element selectors inside a component, wrap everything in `:where()`: + +```less +// CORRECT — (0,1,0) +.d-table { + :where(tbody tr:last-of-type) { + :where(td, th) { border-block-end-width: 0; } + } +} + +// WRONG — (0,2,3) +.d-table { + tbody tr:last-of-type { + td, th { border-block-end-width: 0; } + } +} +``` + +## Do NOT Wrap These + +- **The host component class** — `.d-table` must retain its (0,1,0) as the anchor selector +- **BEM modifier classes** — `.d-table--striped` needs its specificity to override base styles +- **State pseudo-classes** — `:hover`, `:disabled`, `:focus-visible`, `:active`, `:checked` where specificity ordering is functional (e.g., `:not(:disabled):hover` must beat `:disabled`) +- **`:not()` / `:has()` containing class selectors** — the specificity reflects semantic complexity, not accidental inflation + +## Nesting Depth + +- **1 class** (0,1,0): Default. Covers most rules. +- **2 classes** (0,2,0): Acceptable when a parent modifier must affect children (e.g., `.d-tablist--inverted .d-tab`). Prefer CSS custom property overrides when possible. +- **3+ classes**: Avoid. Refactor to use CSS custom properties or restructure the component. + +## `@layer` Context + +All component styles are inside `@layer dialtone.components`. This means: + +- Unlayered consumer CSS always wins regardless of specificity +- Within the layer, lower specificity = easier to extend and override +- Keeping selectors flat benefits consumers who style within the same layer + +## Anti-Patterns + +- No `#id` selectors +- No bare element selectors without `:where()` wrapping +- No `!important` in component styles (reserved for utility classes) +- No qualifying element selectors on classes (e.g., `div.d-banner` — just use `.d-banner`) + +## Reference + +Use [Specificity Calculator](https://specificity.keegan.st/) to verify selector specificity values. It supports CSS Selectors Level 4 including `:where()`, `:is()`, and `:has()`. diff --git a/apps/dialtone-documentation/docs/.vuepress/theme/assets/less/dialtone-docs.less b/apps/dialtone-documentation/docs/.vuepress/theme/assets/less/dialtone-docs.less index c395af47a9..9a1a243609 100644 --- a/apps/dialtone-documentation/docs/.vuepress/theme/assets/less/dialtone-docs.less +++ b/apps/dialtone-documentation/docs/.vuepress/theme/assets/less/dialtone-docs.less @@ -176,7 +176,7 @@ html body { color: var(--dt-color-foreground-secondary); font-size: var(--dt-font-size-300); - strong { + :where(strong) { color: var(--dt-color-foreground-primary); } } @@ -189,19 +189,19 @@ html body { padding-block-end: var(--dt-size-400); } - .DocSearch-Hits mark { + .DocSearch-Hits :where(mark) { padding: var(--dt-size-200); color: var(--dt-color-foreground-primary); background: var(--dt-color-surface-warning); } - .DocSearch-Hit a { + .DocSearch-Hit :where(a) { padding-inline: var(--dt-size-450) var(--dt-size-450); border-radius: var(--dt-size-radius-300); } - .DocSearch-Hit-action svg, - .DocSearch-Hit-icon svg { + .DocSearch-Hit-action :where(svg), + .DocSearch-Hit-icon :where(svg) { inline-size: var(--dt-icon-size-200); block-size: var(--dt-icon-size-200); } @@ -216,7 +216,7 @@ html body { // no overrides } - .DocSearch-Hit[aria-selected="true"] mark { + .DocSearch-Hit[aria-selected="true"] :where(mark) { color: var(--dt-color-foreground-primary) !important; text-decoration: none; } @@ -242,8 +242,8 @@ html body { // no overrides } - .DocSearch-Logo svg { - filter: saturate(0) contrast(0.25); + .DocSearch-Logo :where(svg) { + filter: saturate(0) contrast(.25); } .DocSearch-Commands { @@ -273,7 +273,7 @@ html body { color: var(--dt-color-link-primary); } - .DocSearch-NoResults-Prefill-List li { + .DocSearch-NoResults-Prefill-List :where(li) { list-style-type: none; } } @@ -293,15 +293,15 @@ html body { background-color: var(--dt-shell-color-surface-default); view-transition-name: dialtone-header; - > :first-child { + > :where(:first-child) { justify-self: start; } - > :nth-child(2) { + > :where(:nth-child(2)) { justify-self: center; } - > :last-child { + > :where(:last-child) { justify-self: end; } @@ -417,7 +417,7 @@ a.header-anchor { .d-docsite--header-2, .d-docsite--header-3 { - &:hover a.header-anchor { + &:hover :where(a).header-anchor { opacity: 1; } } @@ -457,7 +457,7 @@ a.header-anchor { } } -code { +:where(code) { .d-docsite--paragraph &, .d-docsite--unordered-list &, .d-docsite--ordered-list &, @@ -563,7 +563,7 @@ code { .d-popover { block-size: 100%; - > div { + > :where(div) { block-size: 100%; } } @@ -633,7 +633,7 @@ code { block-size: var(--dt-size-650); margin-block-end: var(--dt-size-300); - svg { + :where(svg) { inline-size: 100%; block-size: auto; } @@ -642,7 +642,7 @@ code { .dialtone-icon-card__icon--autosize { margin-block-end: var(--dt-size-300); - svg { + :where(svg) { inline-size: 100%; block-size: auto; } @@ -882,17 +882,17 @@ code { // ---------------------------------------------------------------------------- .dialtone-definition { - > dt { + > :where(dt) { margin-block-start: var(--dt-size-500); font-weight: var(--dt-font-weight-bold); } - > dd { + > :where(dd) { max-inline-size: 72ch; margin: 0; } - code { + :where(code) { font-weight: var(--dt-font-weight-normal); } } diff --git a/apps/dialtone-documentation/docs/.vuepress/theme/assets/less/dialtone-syntax.less b/apps/dialtone-documentation/docs/.vuepress/theme/assets/less/dialtone-syntax.less index c8373ea8e6..0ef0d400e3 100644 --- a/apps/dialtone-documentation/docs/.vuepress/theme/assets/less/dialtone-syntax.less +++ b/apps/dialtone-documentation/docs/.vuepress/theme/assets/less/dialtone-syntax.less @@ -9,12 +9,12 @@ div[class*="language-"] { background-color: var(--dt-color-surface-secondary); border-radius: var(--dt-size-500); - & pre { + & :where(pre) { margin: 0; padding: var(--dt-size-450); padding-block-start: var(--dt-size-550); - & code { + & :where(code) { padding: 0; color: var(--dt-color-foreground-secondary); background-color: unset; @@ -59,6 +59,11 @@ pre[class*="language-"] { user-select: initial; } +div[class*=language-] pre { + font: var(--dt-text-code-sm); + line-height: var(--dt-font-line-height-400); +} + @media print { code[class*="language-"], pre[class*="language-"] { @@ -156,7 +161,7 @@ pre[class*="language-"].line-numbers { counter-reset: linenumber; } -pre[class*="language-"].line-numbers > code { +pre[class*="language-"].line-numbers > :where(code) { position: relative; white-space: inherit; } @@ -173,7 +178,7 @@ pre[class*="language-"].line-numbers > code { pointer-events: none; } -.line-numbers-rows > span { +.line-numbers-rows > :where(span) { display: block; counter-increment: linenumber; @@ -197,22 +202,22 @@ pre[class*="language-"].line-numbers > code { pointer-events: none; } -.command-line-prompt > span::before { +.command-line-prompt > :where(span)::before { display: block; padding-inline-end: var(--dt-size-400); color: var(--dt-color-black-600); content: ' '; } -.command-line-prompt > span[data-user]::before { +.command-line-prompt > :where(span[data-user])::before { content: "[" attr(data-user) "@" attr(data-host) "] $"; } -.command-line-prompt > span[data-user="root"]::before { +.command-line-prompt > :where(span[data-user="root"])::before { content: "[" attr(data-user) "@" attr(data-host) "] #"; } -.command-line-prompt > span[data-prompt]::before { +.command-line-prompt > :where(span[data-prompt])::before { content: attr(data-prompt); } diff --git a/apps/dialtone-documentation/docs/.vuepress/theme/components/SidebarItem.vue b/apps/dialtone-documentation/docs/.vuepress/theme/components/SidebarItem.vue index e6c4f53a2e..8ee9e0122e 100644 --- a/apps/dialtone-documentation/docs/.vuepress/theme/components/SidebarItem.vue +++ b/apps/dialtone-documentation/docs/.vuepress/theme/components/SidebarItem.vue @@ -99,7 +99,7 @@ kind="muted" label-class="d-jc-flex-start d-ta-left d-fw-normal" :class="[ - 'dialtone-shell-btn d-w100p', + 'dialtone-shell-btn d-w100p d-tw-pretty', { 'd-pl48': depth === 0 }, { 'd-pl64': depth === 1 }, ]" diff --git a/apps/dialtone-documentation/docs/.vuepress/theme/components/TocItem.vue b/apps/dialtone-documentation/docs/.vuepress/theme/components/TocItem.vue index bc9959c99b..f1139a72a2 100644 --- a/apps/dialtone-documentation/docs/.vuepress/theme/components/TocItem.vue +++ b/apps/dialtone-documentation/docs/.vuepress/theme/components/TocItem.vue @@ -1,7 +1,7 @@ diff --git a/apps/dialtone-documentation/docs/_data/site-nav.json b/apps/dialtone-documentation/docs/_data/site-nav.json index ea6c9c2bd1..a8d5d809f1 100644 --- a/apps/dialtone-documentation/docs/_data/site-nav.json +++ b/apps/dialtone-documentation/docs/_data/site-nav.json @@ -341,10 +341,6 @@ "text": "Input", "link": "/components/input.html" }, - { - "text": "Input Group", - "link": "/components/input-group.html" - }, { "text": "Item Layout", "link": "/components/item-layout.html" @@ -826,6 +822,10 @@ "text": "Text Transform", "link": "/utilities/typography/text-transform.html" }, + { + "text": "Text Wrap", + "link": "/utilities/typography/text-wrap.html" + }, { "text": "Vertical Alignment", "link": "/utilities/typography/vertical-align.html" diff --git a/apps/dialtone-documentation/docs/components/input-group.md b/apps/dialtone-documentation/docs/components/input-group.md index ca11991ed4..bbba1ceed8 100644 --- a/apps/dialtone-documentation/docs/components/input-group.md +++ b/apps/dialtone-documentation/docs/components/input-group.md @@ -1,7 +1,7 @@ --- title: Input Group description: Input Groups are convenience components for a grouping of related inputs. While each input within the group could be independent, the v-model on the group provides a convenient interface for determining the current state of the group. -status: ready +status: deprecated thumb: true image: assets/images/components/input.png storybook: https://dialtone.dialpad.com/vue/?path=/story/components-input-group--default diff --git a/apps/dialtone-documentation/docs/components/link.md b/apps/dialtone-documentation/docs/components/link.md index bdab17695c..1eded95f84 100644 --- a/apps/dialtone-documentation/docs/components/link.md +++ b/apps/dialtone-documentation/docs/components/link.md @@ -71,45 +71,54 @@ vueCode=' ' showHtmlWarning /> -### Inverted +### No underline - - - Inverted base link - Inverted danger link - Inverted success link - Inverted warning link - Inverted muted link - Inverted mention link - +This inverts the underline behavior. With `underline="false"`, the link will not have an underline by default, but will show one on hover. + + + No underline link -### No underline +### Inverted -This inverts the underline behavior. With `underline="false"`, the link will not have an underline by default, but will show one on hover. + + The inverted prop has been deprecated in favor of using DtModeIsland as a wrapper. + + +In place of the inverted prop, use the DtModeIsland component as a wrapper. - No underline link + + + Base link + Danger link + Success link + Warning link + Muted link + Mention link + + diff --git a/apps/dialtone-documentation/docs/components/mode-island.md b/apps/dialtone-documentation/docs/components/mode-island.md index 595f8874b7..f4d3d5ed30 100644 --- a/apps/dialtone-documentation/docs/components/mode-island.md +++ b/apps/dialtone-documentation/docs/components/mode-island.md @@ -273,7 +273,7 @@ vueCode=' ' showHtmlWarning /> -## Custom element +## Render as Polymorphic rendering via `as` prop—controls which HTML element wraps content. Ensures proper document structure and semantic markup. Example values: `section` for thematic grouping, `article` for self-contained content. Defaults to `div` where semantics aren't a concern. @@ -325,12 +325,60 @@ vueCode=' ' /> +## Custom background + +The background surface of a Mode Island defaults to the root surface color. To override, use a CSS Utility class. + + + + + Transparent background, inverted mode island +
+ Button +
+
+
+ + + Default background, inverted mode island +
+ Button +
+
+
+ + + warning background, dark mode island +
+ Button +
+
+
+ + + warning background, light mode island +
+ Button +
+
+
+
+ + + ## Examples ### Callbar - + $refs.callbarExample' vueCode=' - + diff --git a/apps/dialtone-documentation/docs/components/stack.md b/apps/dialtone-documentation/docs/components/stack.md index 1c7c90991f..d18dd6ba19 100644 --- a/apps/dialtone-documentation/docs/components/stack.md +++ b/apps/dialtone-documentation/docs/components/stack.md @@ -185,7 +185,7 @@ vueCode=' ' /> -## As +## Render as The `as` prop controls which HTML element the Stack component renders as. Defaults to `
`, but can be declared as any valid HTML element to ensure semantic and accessible markup. diff --git a/apps/dialtone-documentation/docs/components/table.md b/apps/dialtone-documentation/docs/components/table.md index 5864e0a339..05cd81058c 100644 --- a/apps/dialtone-documentation/docs/components/table.md +++ b/apps/dialtone-documentation/docs/components/table.md @@ -82,50 +82,40 @@ keywords: ["data table", "grid", "rows", "d-table", "DtTable", "dt-table", "data ### Inverted Style -Used when you want to display a data table on a darker background. - - - - - - - - - - - - - - - - - - - - -
Office List
OfficeCountryEmployeesContact
{{ i.office }}{{ i.country }}{{ i.size }}{{ i.contact }}
+ + The d-table--inverted modifier has been deprecated in favor of using DtModeIsland as a wrapper. + + +In place of the d-table--inverted modifier, wrap the table in the DtModeIsland component. + + + + + + + + + + + + + + + + + + + + + +
Office List
OfficeCountryEmployeesContact
{{ i.office }}{{ i.country }}{{ i.size }}{{ i.contact }}
+
```html - - - - - - - - - - - - - - - - - - -
...
............
............
+ + ...
+
``` ### Striped @@ -174,48 +164,34 @@ Used when you want to display a data table on a darker background. ``` - - - - - - - - - - - - - - - - - - - -
Office List
OfficeCountryEmployeesContact
{{ i.office }}{{ i.country }}{{ i.size }}{{ i.contact }}
+ + + + + + + + + + + + + + + + + + + + +
Office List
OfficeCountryEmployeesContact
{{ i.office }}{{ i.country }}{{ i.size }}{{ i.contact }}
+
```html - - - - - - - - - - - - - - - - - - -
...
............
............
+ + ...
+
``` ## Classes diff --git a/apps/dialtone-documentation/docs/components/text.md b/apps/dialtone-documentation/docs/components/text.md index 459bf043da..9f0e849332 100644 --- a/apps/dialtone-documentation/docs/components/text.md +++ b/apps/dialtone-documentation/docs/components/text.md @@ -278,42 +278,37 @@ vueCode=' Use `tone` to declare the text's tone, which will map to a foreground color. By default, the tone is inherited from its parent. - - - primary - secondary - tertiary - muted - disabled - placeholder - success - success-strong - warning - critical - critical-strong - - - primary-inverted - secondary-inverted - tertiary-inverted - muted-inverted - disabled-inverted - placeholder-inverted - success-inverted - success-strong-inverted - warning-inverted - critical-inverted - critical-strong-inverted - + + primary + secondary + tertiary + muted + disabled + placeholder + success + success-strong + warning + critical + critical-strong -## As +## Render as Use `as` to declare the underlying HTML tag that the component should render, independent of the visual styling. Defaults to `span`. diff --git a/apps/dialtone-documentation/docs/guides/contributing/index.md b/apps/dialtone-documentation/docs/guides/contributing/index.md index 97559f7972..aebfca11cf 100644 --- a/apps/dialtone-documentation/docs/guides/contributing/index.md +++ b/apps/dialtone-documentation/docs/guides/contributing/index.md @@ -18,8 +18,14 @@ in our Dialtone repository. ### CSS Architecture +#### Cascade Layers + Dialtone uses [CSS Cascade Layers](../css-layers/) to organize styles into a predictable hierarchy. When contributing CSS, all styles must be wrapped in the appropriate `@layer` block. Read the [CSS Layers Guide](../css-layers/) to understand where to place your styles. +#### Selector Specificity + +All component selectors should target a specificity of `(0,1,0)` — a single class. BEM naming naturally achieves this. Use CSS custom properties for modifier overrides instead of increasing selector depth, and wrap bare element descendants (e.g., `th`, `td`, `a`) in `:where()` to keep specificity flat. See the [dialtone-css CONTRIBUTING guide](https://github.com/dialpad/dialtone/blob/staging/packages/dialtone-css/.github/CONTRIBUTING.md) for full details and examples. + ## Adding Icons and Illustrations If you need to add an icon into Dialtone, here’s how you would go about doing that. diff --git a/apps/dialtone-documentation/docs/scratch.md b/apps/dialtone-documentation/docs/scratch.md index fc4649ab36..61feaaf7d7 100644 --- a/apps/dialtone-documentation/docs/scratch.md +++ b/apps/dialtone-documentation/docs/scratch.md @@ -3,3 +3,876 @@ layout: Blank --- + + + + + + + Scratchpad + + + + + + + + + Disabled Button + + + Not just a matter of applying opacity to whole button, but w/ combination of `color-mix()` and tweaking existing DtButton css variables via `oklch()` of specific properties – separate opacity and saturation for border, bgc, fc, etc. + + + + Disabled + + + + + Place Call + Place Call + Place Call + + + Place Call + Place Call + Place Call + + + Place Call + Place Call + Place Call + + + Place Call + Place Call + + + + + + Button: Leading/Trailing + + + Freeform elements that are rendered before/after the button content. + + + + Leading + + + Trailing + + + Start Icon + + + End Icon + + + `labelClass` + + + Remove leading/trailing class + + + Highlight leading/trailing + + + + + Place Call + + + + + + + Place Call + + + + + + + Place Call + + + + + + + Place Call + + + + + + + Place Call + + + + + + + + + + Sizing update: Button/Input/Select + + + + + + + Button + + + + + + + + Button + + + + + + + + Button + + + + + + + + Button + + + + + + + + Button + + + + + + Input / Select + + + + + `labelClass` + Description + Messages + `messagesClass` + `descriptionClass` + + + + + + + + + + + + + + + + + + + + + + Tabs + + + Just straight up refactor to use DtButton instead of custom markup/style. Use mix of DtButton variants depending on `active`. Uses all DtButton sizes (currently at least). + + + + Borderless + + + Outlined + + + Muted + + + Start Icon + + + End Icon + + + Leading + + + Trailing + + + Select on focus + + + `labelClass` + + + + + +
+ + + Argentina stretches from subtropical forests in the north to glacial landscapes in the south, encompassing the towering Andes mountains and the vast Pampas grasslands in between. + Its cities blend European architectural influences with a vibrant local character, while rural traditions of horsemanship and cattle ranching continue to shape the national identity. + The country is celebrated for its contributions to tango, wine production, and a culinary culture built around shared meals and regional flavors. + + + + + The United States spans a broad continental range, from Atlantic coastlines and Appalachian ridges to Great Plains, Rocky Mountain summits, and Pacific shores beyond. + Major metropolitan areas serve as centers for finance, technology, and the arts, while smaller communities maintain distinct regional customs, dialects, and culinary traditions. + The nation's history of immigration has produced a diverse cultural fabric, with influences from virtually every corner of the globe woven into daily life. + + + + + The United Kingdom comprises England, Scotland, Wales, and Northern Ireland, each with distinct landscapes ranging from chalk cliffs and moors to highland lochs and green valleys. + Its cities layer centuries of history alongside modern architecture, with institutions in education, finance, and governance that have influenced systems around the world. + A strong tradition in literature, theater, and music continues to thrive, supported by public institutions and a widespread culture of creative expression. + + + + + India extends from the Himalayan ranges in the north through fertile river plains to tropical coastlines in the south, supporting an extraordinary range of ecosystems and climates. + Hundreds of languages and traditions coexist across its states and territories, producing one of the most culturally varied societies on earth with deep historical roots. + A growing technology sector and expanding urban centers complement longstanding agricultural and artisan economies that continue to sustain millions of people. + + + + + Canada stretches from the Atlantic to the Pacific and northward into the Arctic, encompassing boreal forests, prairies, mountain ranges, and thousands of lakes and waterways. + Its cities are known for cultural diversity and livability, while vast rural and wilderness areas support forestry, mining, and agriculture across multiple climate zones. + Official bilingualism in English and French reflects a history shaped by Indigenous peoples, European settlement, and ongoing immigration from around the world. + + +
+
+
+ + + Notice / Banner / Toast + + + Updated typography sizing and intelligent icon alignment. Icon margin adjusts based on content layout: title-only, message-only, or title+message. + + + + Notice + +
+ + Default + + Action completed successfully. + + + Please review before proceeding. + + + Something went wrong. Please try again. + + + A neutral notice for general information. + + + + Important + + Visually prominent variant with filled background. + + + Visually prominent variant with filled background. + + + Visually prominent variant with filled background. + + + Visually prominent variant with filled background. + + + + Alignment per internal parts + + + Message only — icon aligns to center when there is a single line of content. + + + When both title and message are present, the icon aligns to the top of the content stack. + + +
+
+ + + Banner + + + + Banners are more prominent than notices. + + + Action completed successfully. + + + Please review before proceeding. + + + Something went wrong. + + + + Important + + Banners are more prominent than notices. + + + Action completed successfully. + + + Please review before proceeding. + + + Something went wrong. + + + + + + Toast + +
+ + Default + + + + + + + + Important + + + + + + + + Alignment per internal parts + + + + +
+
+
+ + + Radio / Checkbox + + + + + Description + Disabled + `labelClass` + Messages + `messagesClass` + `descriptionClass` + + + + Checkbox + + + + + + Radio + + + + + + +
+
diff --git a/apps/dialtone-documentation/docs/utilities/borders/radius.md b/apps/dialtone-documentation/docs/utilities/borders/radius.md index 06d331fb90..20bd9468ee 100644 --- a/apps/dialtone-documentation/docs/utilities/borders/radius.md +++ b/apps/dialtone-documentation/docs/utilities/borders/radius.md @@ -14,7 +14,7 @@ Use `d-bar{n}` to change the border radius on all corners of your element. :direction="{ 'default': 'column', 'md': 'row' }" >
@@ -28,6 +28,7 @@ Use `d-bar{n}` to change the border radius on all corners of your element.
...
...
...
+
...
...
...
...
@@ -110,7 +111,7 @@ Use `d-b{a|t|r|b|l}r-circle` to change the border radius of your element to a ci - + .d-b{{ i }}r{{ val }} diff --git a/apps/dialtone-documentation/project.json b/apps/dialtone-documentation/project.json index 6faca841ce..4589e9c1cb 100644 --- a/apps/dialtone-documentation/project.json +++ b/apps/dialtone-documentation/project.json @@ -40,7 +40,10 @@ "cwd": "{projectRoot}", "commands": [ { - "command": "nx watch --projects=dialtone-css,dialtone-tokens,dialtone-vue -- nx run \\$NX_PROJECT_NAME:build" + "command": "nx run dialtone-css:startWatch" + }, + { + "command": "nx watch --projects=dialtone-tokens,dialtone-vue -- nx run \\$NX_PROJECT_NAME:build" }, { "command": "node scripts/generate-raw-markdown.mjs && vuepress dev docs" diff --git a/packages/dialtone-css/.github/CONTRIBUTING.md b/packages/dialtone-css/.github/CONTRIBUTING.md index ac8a81cdf0..46b9a91b49 100644 --- a/packages/dialtone-css/.github/CONTRIBUTING.md +++ b/packages/dialtone-css/.github/CONTRIBUTING.md @@ -33,6 +33,85 @@ Component class names use the [Block Element Modifier (BEM)](http://getbem.com/n .d-banner--success {} // Modifier ``` +### Selector specificity + +Keeping selector specificity low and predictable is critical for a design system. Consumers need to be able to override component styles without resorting to specificity escalation or `!important`. + +#### Why BEM keeps specificity flat + +Every BEM selector — block, element, or modifier — is a single class, which means a specificity of `(0,1,0)`. This is the target for all component selectors. When every rule has the same specificity, the cascade is determined by source order alone, which is predictable and easy to reason about. + +#### Use CSS custom properties for modifier overrides + +When a modifier needs to change a property, prefer overriding a CSS custom property rather than targeting a child with a higher-specificity selector: + +```less +// Preferred — both selectors are (0,1,0) +.d-banner { + --banner-color-background: var(--dt-color-surface-primary); + background-color: var(--banner-color-background); +} + +.d-banner--success { + --banner-color-background: var(--dt-color-surface-success); +} + +// Avoid — inflates to (0,2,0) +.d-banner--success .d-banner__icon { + color: green; +} +``` + +#### Element-type descendants: wrap in `:where()` + +Some components must style bare HTML elements (e.g., `th`, `td`, `a`, `button`, `option` inside a table, toast, or select). These add specificity that makes overrides harder. Wrap them in `:where()` to zero out their specificity contribution: + +```less +// Correct — specificity stays at (0,1,0) +.d-table { + :where(th, td) { + padding: var(--dt-size-500); + } +} + +// Avoid — specificity is (0,1,1), harder to override +.d-table { + th, td { + padding: var(--dt-size-500); + } +} +``` + +The same applies to structural pseudo-classes on element selectors: + +```less +// Correct — (0,1,0) +.d-table :where(tbody tr:last-of-type) :where(td, th) { + border-block-end-width: 0; +} + +// Avoid — (0,2,3) +.d-table tbody tr:last-of-type td, th { + border-block-end-width: 0; +} +``` + +Do **not** wrap the component class itself, BEM modifiers, or state pseudo-classes (`:hover`, `:disabled`, `:focus-visible`) in `:where()` — their specificity is intentional. + +#### Quick reference + +| Selector | Specificity | Verdict | +|----------|-------------|---------| +| `.d-banner` | (0,1,0) | Ideal | +| `.d-banner__dialog` | (0,1,0) | Ideal | +| `.d-banner--success` | (0,1,0) | Ideal | +| `.d-table :where(th)` | (0,1,0) | Correct — element wrapped | +| `.d-table th` | (0,1,1) | Avoid — bare element inflates specificity | +| `.d-tablist--inverted .d-tab` | (0,2,0) | Acceptable — parent modifier affecting child | +| `.d-notice.d-notice--truncate .d-notice__content .d-notice__title` | (0,4,0) | Avoid — refactor to use CSS custom properties | + +Use the [Specificity Calculator](https://specificity.keegan.st/) to verify selector specificity values when in doubt. It supports CSS Selectors Level 4 including `:where()`, `:is()`, and `:has()`. + ### Immutable utility classes All of our utility classes are set to `!important`. This is because they are designed to be immutable, and `!important` is the best way we have of achieving immutability in CSS. Utility classes should only be applied at the application level and not within Dialtone Vue components. diff --git a/packages/dialtone-css/gulpfile.cjs b/packages/dialtone-css/gulpfile.cjs index 1f969ee676..aec2a39841 100644 --- a/packages/dialtone-css/gulpfile.cjs +++ b/packages/dialtone-css/gulpfile.cjs @@ -35,7 +35,7 @@ const path = require('path'); // @@ STYLES const postCSS = settings.styles ? require('gulp-postcss') : null; // crawls .less dependencies for incremental building -const postCSSNano = settings.styles ? require('cssnano') : null; +const postCSSNano = settings.styles ? require('cssnano')({ preset: ['default', { calc: false }] }) : null; const less = settings.styles ? require('gulp-less') : null; const postCSSDialtoneGenerator = settings.styles ? require('./postcss/dialtone-generators.cjs') : null; const sourcemaps = settings.styles ? require('gulp-sourcemaps') : null; @@ -419,6 +419,9 @@ exports.watch = series( watchFiles, ); +// start watching without a prior clean or full build (assumes build already ran). +exports.startWatch = series(watchFiles); + // -- CONVERT WEBFONTS exports.fonts = series( webfonts, diff --git a/packages/dialtone-css/lib/build/less/components/card.less b/packages/dialtone-css/lib/build/less/components/card.less index c55d13d357..ae01cfc2e6 100644 --- a/packages/dialtone-css/lib/build/less/components/card.less +++ b/packages/dialtone-css/lib/build/less/components/card.less @@ -53,6 +53,7 @@ // ---------------------------------------------------------------------------- .d-card__footer { display: flex; + gap: var(--dt-size-400); align-items: center; padding: 0 var(--dt-size-500) var(--dt-size-500); } diff --git a/packages/dialtone-css/lib/build/less/components/selects.less b/packages/dialtone-css/lib/build/less/components/selects.less index 834f50066f..600f23c817 100644 --- a/packages/dialtone-css/lib/build/less/components/selects.less +++ b/packages/dialtone-css/lib/build/less/components/selects.less @@ -54,7 +54,7 @@ ._input(); // select options can be styled on windows - & option { + & :where(option) { color: var(--dt-color-foreground-secondary); background-color: var(--dt-color-surface-secondary); } diff --git a/packages/dialtone-css/lib/build/less/components/table.less b/packages/dialtone-css/lib/build/less/components/table.less index bdca4c3d57..51694b940d 100644 --- a/packages/dialtone-css/lib/build/less/components/table.less +++ b/packages/dialtone-css/lib/build/less/components/table.less @@ -34,7 +34,7 @@ border-spacing: 0; // Caption styles - .d-table__caption { + &__caption { margin-block-end: var(--dt-size-400); // 8 color: var(--table-th-color-text); font-weight: var(--dt-font-weight-bold); @@ -43,11 +43,11 @@ } // Table Head Styles - thead { + :where(thead) { border-block-end: var(--dt-size-200) solid var(--table-color-border); // Column Header Styles - th { + :where(th) { font-size: var(--table-th-font-size); line-height: var(--dt-font-line-height-100); text-transform: uppercase; @@ -55,38 +55,35 @@ } // Header Styles - th { + :where(th) { color: var(--table-th-color-text); font-weight: var(--dt-font-weight-bold); } // Cell Styles - td { + :where(td) { color: var(--table-td-color-text); } // Header & Cell Styles - th, - td { + :where(th, td) { padding: var(--dt-size-500); // 16 text-align: start; border-block-end: var(--dt-size-100) solid var(--table-color-border); } // Nested Table Body, First Row Styles, used internally on the Dialtone site - tbody + tbody tr:first-of-type { + :where(tbody + tbody tr:first-of-type) { // Header & Cell Styles - td, - th { + :where(td, th) { border-block-start: var(--dt-size-200) solid var(--table-color-border); } } // Last Table Row Styles - tbody tr:last-of-type { + :where(tbody tr:last-of-type) { // Header & Cell Styles - td, - th { + :where(td, th) { border-block-end-width: 0; } } @@ -106,13 +103,13 @@ // ---------------------------------------------------------------------------- .d-table--striped { // Row Styles - tr:nth-child(even) { + :where(tr:nth-child(even)) { background-color: var(--dt-color-surface-secondary-opaque); } &.d-table--inverted { // Row Styles - tr:nth-child(even) { + :where(tr:nth-child(even)) { background-color: oklch(from var(--dt-color-surface-primary) l c h / 0.1); } } diff --git a/packages/dialtone-css/lib/build/less/dialtone.less b/packages/dialtone-css/lib/build/less/dialtone.less index 31b1750b9b..0dd77691ed 100644 --- a/packages/dialtone-css/lib/build/less/dialtone.less +++ b/packages/dialtone-css/lib/build/less/dialtone.less @@ -17,13 +17,20 @@ @import 'components/root-layout'; @import 'components/stack'; @import 'components/text'; +@import 'components/link'; @import 'components/item-layout'; @import 'components/avatar'; -@import 'components/badge'; -@import 'components/breadcrumbs'; @import 'components/button'; -@import 'components/card'; +@import 'components/badge'; @import 'components/chip'; +@import 'components/keyboard-shortcut'; +@import 'components/mode-island'; +@import 'components/table'; +@import 'components/toggle'; +@import 'components/presence'; +@import 'components/icon'; +@import 'components/emoji'; +@import 'components/emoji-text-wrapper'; @import 'components/codeblock'; @import 'components/collapsible'; @import 'components/combobox'; @@ -31,16 +38,17 @@ @import 'components/dropdown'; @import 'components/description-list'; @import 'components/empty-state'; -@import 'components/forms'; -@import 'components/image-viewer'; @import 'components/input'; -@import 'components/keyboard-shortcut'; -@import 'components/link'; +@import 'components/selects'; +@import 'components/image-viewer'; +@import 'components/forms'; +@import 'components/tooltip'; +@import 'components/card'; +@import 'components/breadcrumbs'; @import 'components/list-group'; // Dialtone 5 shim @import 'components/list-item-group'; @import 'components/list-item'; @import 'components/loader'; -@import 'components/mode-island'; @import 'components/modal'; @import 'components/notice'; @import 'components/toast'; @@ -50,17 +58,9 @@ @import 'components/radio-checkbox'; @import 'components/rich-text-editor'; @import 'components/scroller'; -@import 'components/selects'; @import 'components/skeleton'; -@import 'components/table'; @import 'components/tabs'; -@import 'components/tooltip'; -@import 'components/toggle'; -@import 'components/presence'; -@import 'components/icon'; @import 'components/scrollbar'; -@import 'components/emoji'; -@import 'components/emoji-text-wrapper'; @import 'components/emoji-picker'; @import 'components/filter-pill'; diff --git a/packages/dialtone-css/postcss/constants.cjs b/packages/dialtone-css/postcss/constants.cjs index 28ca9696b3..90ec181fbe 100644 --- a/packages/dialtone-css/postcss/constants.cjs +++ b/packages/dialtone-css/postcss/constants.cjs @@ -27,6 +27,7 @@ module.exports = { 1: 'radius-100', 2: 'radius-200', 4: 'radius-300', + 6: 'radius-350', 8: 'radius-400', 12: 'radius-450', 16: 'radius-500', diff --git a/packages/dialtone-css/project.json b/packages/dialtone-css/project.json index b5362e6ac2..66d9202fab 100644 --- a/packages/dialtone-css/project.json +++ b/packages/dialtone-css/project.json @@ -33,6 +33,20 @@ "parallel": false } }, + "buildWatch": { + "executor": "nx:run-commands", + "options": { + "cwd": "{projectRoot}", + "command": "gulp buildWatch" + } + }, + "startWatch": { + "executor": "nx:run-commands", + "options": { + "cwd": "{projectRoot}", + "command": "gulp startWatch" + } + }, "watch": { "executor": "nx:run-commands", "dependsOn": [ diff --git a/packages/dialtone-tokens/tokens/theme/dp/dark.json b/packages/dialtone-tokens/tokens/theme/dp/dark.json index ff4762235b..7d41643bdf 100644 --- a/packages/dialtone-tokens/tokens/theme/dp/dark.json +++ b/packages/dialtone-tokens/tokens/theme/dp/dark.json @@ -31,7 +31,7 @@ "type": "color" }, "success": { - "value": "{color.green.700}", + "value": "{color.green.600}", "type": "color" }, "critical-strong": { @@ -39,7 +39,7 @@ "type": "color" }, "success-strong": { - "value": "{color.green.900}", + "value": "{color.green.800}", "type": "color" }, "primary-inverted": { @@ -174,11 +174,11 @@ "type": "color" }, "warning-strong": { - "value": "{color.gold.800}", + "value": "{color.gold.700}", "type": "color" }, "success-strong": { - "value": "{color.green.700}", + "value": "{color.green.600}", "type": "color" }, "info-strong": { @@ -570,7 +570,7 @@ "type": "color" }, "success": { - "value": "{color.green.800}", + "value": "{color.green.600}", "type": "color" }, "brand": { @@ -598,7 +598,7 @@ "type": "color" }, "success-strong": { - "value": "{color.green.700}", + "value": "{color.green.800}", "type": "color" }, "warning-strong": { @@ -1091,6 +1091,14 @@ }, "positive": { "primary": { + "default": { + "value": "{color.green.600}", + "type": "color" + }, + "hover": { + "value": "{color.green.700}", + "type": "color" + }, "active": { "value": "{action.color.background.positive.primary.default}", "type": "color" diff --git a/packages/dialtone-vue/components/breadcrumbs/breadcrumb_item.stories.js b/packages/dialtone-vue/components/breadcrumbs/breadcrumb_item.stories.js index 2b00e324a6..dc2d542e02 100644 --- a/packages/dialtone-vue/components/breadcrumbs/breadcrumb_item.stories.js +++ b/packages/dialtone-vue/components/breadcrumbs/breadcrumb_item.stories.js @@ -22,6 +22,9 @@ export const argTypesData = { }, control: 'text', }, + inverted: { + table: { disable: true }, + }, }; // Story Collection diff --git a/packages/dialtone-vue/components/breadcrumbs/breadcrumbs.stories.js b/packages/dialtone-vue/components/breadcrumbs/breadcrumbs.stories.js index 0b34ec71c2..755eb66dff 100644 --- a/packages/dialtone-vue/components/breadcrumbs/breadcrumbs.stories.js +++ b/packages/dialtone-vue/components/breadcrumbs/breadcrumbs.stories.js @@ -51,6 +51,9 @@ export const argTypesData = { }, }, }, + inverted: { + table: { disable: true }, + }, }; // Story Collection diff --git a/packages/dialtone-vue/components/breadcrumbs/breadcrumbs_variants.story.vue b/packages/dialtone-vue/components/breadcrumbs/breadcrumbs_variants.story.vue index 2ec3d93b3e..a53902bd2a 100644 --- a/packages/dialtone-vue/components/breadcrumbs/breadcrumbs_variants.story.vue +++ b/packages/dialtone-vue/components/breadcrumbs/breadcrumbs_variants.story.vue @@ -4,13 +4,6 @@ class="d-py16 d-px12" :breadcrumbs="$attrs.breadcrumbs" /> -
- -
- diff --git a/packages/dialtone-vue/components/card/card_default.story.vue b/packages/dialtone-vue/components/card/card_default.story.vue index c49dbd80b0..a8e6e9f219 100644 --- a/packages/dialtone-vue/components/card/card_default.story.vue +++ b/packages/dialtone-vue/components/card/card_default.story.vue @@ -11,10 +11,17 @@ v-if="showHeader && variants" #header > -

Header

+ + Header + @@ -72,6 +74,7 @@ import DtCard from './card.vue'; import { DtIcon } from '@/components/icon'; import { DtButton } from '@/components/button'; +import { DtText } from '@/components/text'; export default { name: 'DtCardDefault', @@ -79,6 +82,7 @@ export default { DtCard, DtButton, DtIcon, + DtText, }, props: { diff --git a/packages/dialtone-vue/components/filter_pill/filter_pill.stories.js b/packages/dialtone-vue/components/filter_pill/filter_pill.stories.js index debd40a820..2864cc4ad0 100644 --- a/packages/dialtone-vue/components/filter_pill/filter_pill.stories.js +++ b/packages/dialtone-vue/components/filter_pill/filter_pill.stories.js @@ -113,7 +113,7 @@ export const Default = { render: DefaultTemplate, decorators: [ () => ({ - template: `
`, + template: `
`, }), ], }; diff --git a/packages/dialtone-vue/components/hovercard/hovercard.stories.js b/packages/dialtone-vue/components/hovercard/hovercard.stories.js index e321f030bc..d8ff0c8d35 100644 --- a/packages/dialtone-vue/components/hovercard/hovercard.stories.js +++ b/packages/dialtone-vue/components/hovercard/hovercard.stories.js @@ -172,11 +172,7 @@ const DefaultTemplate = (args, { argTypes }) => createTemplateFromVueFile( export const Default = { render: DefaultTemplate, decorators: [() => ({ - template: ` -
- -
-
`, + template: `
`, })], args: {}, @@ -190,8 +186,7 @@ const ManyTemplate = (args, { argTypes }) => createTemplateFromVueFile( export const Many = { render: ManyTemplate, decorators: [() => ({ - template: `
-
`, + template: `
`, })], args: { ...Default.args, offset: [0, 5] }, }; @@ -204,11 +199,7 @@ const InputTemplate = (args, { argTypes }) => createTemplateFromVueFile( export const WithInput = { render: InputTemplate, decorators: [() => ({ - template: ` -
- -
-
`, + template: `
`, })], args: { ...Default.args, offset: [0, 5] }, }; diff --git a/packages/dialtone-vue/components/icon/icon_variants.story.vue b/packages/dialtone-vue/components/icon/icon_variants.story.vue index 361caad445..c5bd30176a 100644 --- a/packages/dialtone-vue/components/icon/icon_variants.story.vue +++ b/packages/dialtone-vue/components/icon/icon_variants.story.vue @@ -5,15 +5,25 @@ :key="category" >

- + + + + +

@@ -21,10 +31,11 @@ diff --git a/packages/dialtone-vue/components/root_layout/root_layout.vue b/packages/dialtone-vue/components/root_layout/root_layout.vue index a224093b4c..aac04f0a86 100644 --- a/packages/dialtone-vue/components/root_layout/root_layout.vue +++ b/packages/dialtone-vue/components/root_layout/root_layout.vue @@ -78,7 +78,8 @@ export default { }, /** - * DEPRECATED: set the height of the inner element instead. + * Set the height of the inner element instead. + * @deprecated */ headerHeight: { type: String, @@ -119,7 +120,8 @@ export default { }, /** - * DEPRECATED: set the width of the inner element instead. + * Set the width of the inner element instead. + * @deprecated */ sidebarWidth: { type: String, @@ -145,7 +147,8 @@ export default { }, /** - * DEPRECATED: set the height of the inner element instead. + * Set the height of the inner element instead. + * @deprecated */ footerHeight: { type: String, diff --git a/packages/dialtone-vue/components/scroller/scroller_default.story.vue b/packages/dialtone-vue/components/scroller/scroller_default.story.vue index c0f2d5f385..86497beb93 100644 --- a/packages/dialtone-vue/components/scroller/scroller_default.story.vue +++ b/packages/dialtone-vue/components/scroller/scroller_default.story.vue @@ -129,7 +129,7 @@ function switchAutoScrolling () { .autoscrolling{ display: flex; align-items: center; - div { + :where(div) { background-color: red; inline-size: 5px; block-size: 5px; diff --git a/packages/dialtone-vue/components/scroller/scroller_dynamic.story.vue b/packages/dialtone-vue/components/scroller/scroller_dynamic.story.vue index 50f9d22572..983e126ec6 100644 --- a/packages/dialtone-vue/components/scroller/scroller_dynamic.story.vue +++ b/packages/dialtone-vue/components/scroller/scroller_dynamic.story.vue @@ -126,7 +126,7 @@ function replaceItems () { .autoscrolling{ display: flex; align-items: center; - div { + :where(div) { background-color: red; inline-size: 5px; block-size: 5px; diff --git a/packages/dialtone-vue/components/text/text_variants.story.vue b/packages/dialtone-vue/components/text/text_variants.story.vue index 13fa9b653b..7d93b79ac2 100644 --- a/packages/dialtone-vue/components/text/text_variants.story.vue +++ b/packages/dialtone-vue/components/text/text_variants.story.vue @@ -228,24 +228,6 @@
- -
-
- - - {{ tone }} - - -
-
@@ -400,7 +382,7 @@ > {{ item.wrap.charAt(0).toUpperCase() + item.wrap.slice(1) }} -
+
.feed-item-row { &__default-story { - p { + :where(p) { color: var(--dt-color-foreground-primary); font-size: 15px; font-style: normal; From 3d1d595a6f86687d8cca83a5de6c069d94a9b3f1 Mon Sep 17 00:00:00 2001 From: francisrupert Date: Sat, 7 Mar 2026 09:12:17 -0600 Subject: [PATCH 02/47] migrate to dt-text component --- .../baseComponents/ComponentVueApiTable.vue | 33 +++++++++---------- .../theme/assets/less/dialtone-syntax.less | 6 ++-- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/apps/dialtone-documentation/docs/.vuepress/baseComponents/ComponentVueApiTable.vue b/apps/dialtone-documentation/docs/.vuepress/baseComponents/ComponentVueApiTable.vue index 4379af6ba4..03c5997947 100644 --- a/apps/dialtone-documentation/docs/.vuepress/baseComponents/ComponentVueApiTable.vue +++ b/apps/dialtone-documentation/docs/.vuepress/baseComponents/ComponentVueApiTable.vue @@ -46,17 +46,16 @@ > - - - {{ item.name }} - - -
+ {{ item.name }} + + required -
+
- + {{ item.defaultValue }} - + @@ -89,14 +88,14 @@ | - "{{ value }}" + + "{{ value }}" + - - - {{ item.type }} - - + + {{ item.type }} + {{ item.deprecatedMessage }} diff --git a/apps/dialtone-documentation/docs/.vuepress/theme/assets/less/dialtone-syntax.less b/apps/dialtone-documentation/docs/.vuepress/theme/assets/less/dialtone-syntax.less index 0ef0d400e3..ba92c13819 100644 --- a/apps/dialtone-documentation/docs/.vuepress/theme/assets/less/dialtone-syntax.less +++ b/apps/dialtone-documentation/docs/.vuepress/theme/assets/less/dialtone-syntax.less @@ -33,11 +33,11 @@ div[class*="language-"] { } } -code:not(.d-code--md, .d-code--sm) { +:where(.d-docsite--paragraph > code) { padding: var(--dt-size-200) var(--dt-size-300); color: var(--dt-color-blue-800); - background-color: var(--dt-color-surface-info); - border-radius: var(--dt-size-100); + border-radius: var(--dt-size-200); + border: 1px solid var(--dt-color-border-subtle); user-select: all; } From a97556271ec7769d7ab3bbf320ec6a0186c86de1 Mon Sep 17 00:00:00 2001 From: francisrupert Date: Sat, 7 Mar 2026 09:39:06 -0600 Subject: [PATCH 03/47] migrate ComponentAccessibleTable and ComponentClassTable to dt-text component --- .../ComponentAccessibleTable.vue | 41 +++++++++++++------ .../baseComponents/ComponentClassTable.vue | 37 ++++++++++++----- 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/apps/dialtone-documentation/docs/.vuepress/baseComponents/ComponentAccessibleTable.vue b/apps/dialtone-documentation/docs/.vuepress/baseComponents/ComponentAccessibleTable.vue index 732a81bbb7..f1e0642f05 100644 --- a/apps/dialtone-documentation/docs/.vuepress/baseComponents/ComponentAccessibleTable.vue +++ b/apps/dialtone-documentation/docs/.vuepress/baseComponents/ComponentAccessibleTable.vue @@ -35,19 +35,34 @@ v-for="({ item, applies, description }) in accessible" :key="item" > - - - + + + {{ item }} + + + + + {{ applies }} + + + + + {{ description }} + + diff --git a/apps/dialtone-documentation/docs/.vuepress/baseComponents/ComponentClassTable.vue b/apps/dialtone-documentation/docs/.vuepress/baseComponents/ComponentClassTable.vue index 11c66169bf..998d569cad 100644 --- a/apps/dialtone-documentation/docs/.vuepress/baseComponents/ComponentClassTable.vue +++ b/apps/dialtone-documentation/docs/.vuepress/baseComponents/ComponentClassTable.vue @@ -37,19 +37,34 @@ > - - + + {{ className.startsWith('data-') ? className : `.${className}` }} + + + + + > + {{ applies }} + + + + + {{ description }} + - From a95ff17954b0248f17aafff9ecb0337a1d94f2cb Mon Sep 17 00:00:00 2001 From: francisrupert Date: Sat, 7 Mar 2026 10:08:58 -0600 Subject: [PATCH 04/47] migrate DesignColorTable, NewUtilityClassTable, ThemeColorTable, TokenValue, and Page to dt-text component --- .../baseComponents/DesignColorTable.vue | 16 +++++++-- .../baseComponents/NewUtilityClassTable.vue | 18 +++++----- .../baseComponents/ThemeColorTable.vue | 33 +++++++++++++++---- .../baseComponents/tokens/TokenValue.vue | 8 ++--- .../docs/.vuepress/theme/components/Page.vue | 13 +++++--- 5 files changed, 60 insertions(+), 28 deletions(-) diff --git a/apps/dialtone-documentation/docs/.vuepress/baseComponents/DesignColorTable.vue b/apps/dialtone-documentation/docs/.vuepress/baseComponents/DesignColorTable.vue index cd001a4263..848a85a21c 100644 --- a/apps/dialtone-documentation/docs/.vuepress/baseComponents/DesignColorTable.vue +++ b/apps/dialtone-documentation/docs/.vuepress/baseComponents/DesignColorTable.vue @@ -93,10 +93,20 @@ const colors = processColorsDocs(props.excludedColors, props.classPrefix); - + + {{ color.description }} + - - + + + {{ color.tokenName ? `var(${color.tokenName})` : '-' }} + + + + + {{ color.utilityClass }} + + diff --git a/apps/dialtone-documentation/docs/.vuepress/baseComponents/NewUtilityClassTable.vue b/apps/dialtone-documentation/docs/.vuepress/baseComponents/NewUtilityClassTable.vue index 091b59021f..8854256564 100644 --- a/apps/dialtone-documentation/docs/.vuepress/baseComponents/NewUtilityClassTable.vue +++ b/apps/dialtone-documentation/docs/.vuepress/baseComponents/NewUtilityClassTable.vue @@ -21,16 +21,16 @@ v-for="(value, className) in classes" :key="className" > - - + + + {{ className }} + + + - + + {{ value }} + diff --git a/apps/dialtone-documentation/docs/.vuepress/baseComponents/ThemeColorTable.vue b/apps/dialtone-documentation/docs/.vuepress/baseComponents/ThemeColorTable.vue index e3a505633d..a42853fe4c 100644 --- a/apps/dialtone-documentation/docs/.vuepress/baseComponents/ThemeColorTable.vue +++ b/apps/dialtone-documentation/docs/.vuepress/baseComponents/ThemeColorTable.vue @@ -73,13 +73,16 @@ class="d-w42 d-h42 d-bar-circle d-ba d-bc-subtle" /> -
Ag -
+
- - - - + + + {{ color.section }} + + + + + {{ color.states }} + + + + + {{ color.property }} + + + + + {{ color.variable }} + + diff --git a/apps/dialtone-documentation/docs/.vuepress/baseComponents/tokens/TokenValue.vue b/apps/dialtone-documentation/docs/.vuepress/baseComponents/tokens/TokenValue.vue index d908acc137..a9e4a99e4b 100644 --- a/apps/dialtone-documentation/docs/.vuepress/baseComponents/tokens/TokenValue.vue +++ b/apps/dialtone-documentation/docs/.vuepress/baseComponents/tokens/TokenValue.vue @@ -1,19 +1,19 @@ From b06c80147182e0ecdb8650b3cb82c9a92cd41df0 Mon Sep 17 00:00:00 2001 From: francisrupert Date: Sat, 7 Mar 2026 16:02:59 -0600 Subject: [PATCH 15/47] fix closing tag --- .../docs/utilities/backgrounds/gradients.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dialtone-documentation/docs/utilities/backgrounds/gradients.md b/apps/dialtone-documentation/docs/utilities/backgrounds/gradients.md index 4b7cc8ff34..7e74795c0e 100644 --- a/apps/dialtone-documentation/docs/utilities/backgrounds/gradients.md +++ b/apps/dialtone-documentation/docs/utilities/backgrounds/gradients.md @@ -143,7 +143,7 @@ The starting stop (`d-bgg-from-{color}`) should be declared. Optionally an endin -
+ From 6e9ac56d5fef80bc9a3969fce7173dd452e5c467 Mon Sep 17 00:00:00 2001 From: francisrupert Date: Sat, 7 Mar 2026 16:31:05 -0600 Subject: [PATCH 16/47] rename split button subcomponents from alpha/omega to start/end --- .../lib/build/less/components/button.less | 113 +++++++------ ..._button-omega.vue => split_button-end.vue} | 6 +- ...utton-alpha.vue => split_button-start.vue} | 6 +- .../split_button/split_button.test.js | 160 +++++++++--------- .../components/split_button/split_button.vue | 40 ++--- 5 files changed, 171 insertions(+), 154 deletions(-) rename packages/dialtone-vue/components/split_button/{split_button-omega.vue => split_button-end.vue} (94%) rename packages/dialtone-vue/components/split_button/{split_button-alpha.vue => split_button-start.vue} (97%) diff --git a/packages/dialtone-css/lib/build/less/components/button.less b/packages/dialtone-css/lib/build/less/components/button.less index 908b68f705..9738e703b3 100644 --- a/packages/dialtone-css/lib/build/less/components/button.less +++ b/packages/dialtone-css/lib/build/less/components/button.less @@ -719,12 +719,15 @@ .d-popover, .d-popover div, - .d-popover &__omega { + .d-popover &__omega, + .d-popover &__end { block-size: 100%; } &__alpha, - &__omega { + &__omega, + &__start, + &__end { position: relative; &:focus-visible { @@ -732,12 +735,27 @@ } } - &__alpha { + &__alpha, + &__start { flex-grow: 1; padding-inline-end: calc(var(--button-padding-x) + var(--dt-size-200)); + + &:not(.d-btn--outlined) { + border-inline-end: 0; + } + + &:disabled:not(.d-btn--outlined), + &:disabled:not(.d-btn--primary), + &.d-btn--primary, + &.d-btn--outlined { + border-inline-end: 0; + border-start-end-radius: 0; + border-end-end-radius: 0; + } } - &__omega { + &__omega, + &__end { // vertical divider between the two buttons &::before { position: absolute; @@ -751,20 +769,27 @@ opacity: var(--dt-opacity-600); // soften it content: ""; - // soften divider for muted outlined - .d-split-btn:has(.d-btn--outlined.d-btn--muted, .d-btn--outlined.d-btn--inverted) & { + // soften divider for muted outlined + .d-split-btn:has(.d-btn--outlined.d-btn--muted, + .d-btn--outlined.d-btn--inverted) & { opacity: var(--dt-opacity-300); } // hide divider line when hovering or focusing any button within the split button - .d-split-btn:has(:focus-visible, :hover, .d-btn--active) & { + .d-split-btn:has(:focus-visible, + :hover, + .d-btn--active) & { opacity: 0; } // DO NOT hide divider line for primary, outlined, or disabled buttons .d-split-btn:has(:disabled) &, - .d-split-btn:has(.d-btn--outlined:focus-visible, .d-btn--outlined:hover, .d-btn--outlined.d-btn--active) &, - .d-split-btn:has(.d-btn--primary:focus-visible, .d-btn--primary:hover, .d-btn--primary.d-btn--active) & { + .d-split-btn:has(.d-btn--outlined:focus-visible, + .d-btn--outlined:hover, + .d-btn--outlined.d-btn--active) &, + .d-split-btn:has(.d-btn--primary:focus-visible, + .d-btn--primary:hover, + .d-btn--primary.d-btn--active) & { opacity: var(--dt-opacity-600); } } @@ -779,59 +804,51 @@ &.d-btn--outlined::before { inset-block: var(--dt-size-50-negative); } - } - - &__alpha:disabled:not(.d-btn--outlined), - &__alpha:disabled:not(.d-btn--primary), - &__alpha.d-btn--primary, - &__alpha.d-btn--outlined { - border-inline-end: 0; - border-start-end-radius: 0; - border-end-end-radius: 0; - } - &__alpha:not(.d-btn--outlined) { - border-inline-end: 0; - } - - &__omega:not(.d-btn--outlined) { - border-inline-start: 0; - } + &:not(.d-btn--outlined) { + border-inline-start: 0; + } - &__omega:not(.d-btn--outlined, .d-btn--primary) > * { - // yes, I'm pushing a half-pixel over just for this variant - // to ensure icon is centered - transform: translateX(var(--dt-size-50)); - } + &:not(.d-btn--outlined, .d-btn--primary) > * { + // yes, I'm pushing a half-pixel over just for this variant + // to ensure icon is centered + transform: translateX(var(--dt-size-50)); + } - &__omega:disabled:not(.d-btn--outlined), - &__omega:disabled:not(.d-btn--primary), - &__omega.d-btn--primary, - &__omega.d-btn--outlined { - border-inline-start: 0; - border-start-start-radius: 0; - border-end-start-radius: 0; + &:disabled:not(.d-btn--outlined), + &:disabled:not(.d-btn--primary), + &.d-btn--primary, + &.d-btn--outlined { + border-inline-start: 0; + border-start-start-radius: 0; + border-end-start-radius: 0; + } } // adjust x-padding for icon-only button at each size - &__omega--xs.d-btn--icon-only { - --button-padding-x: var(--dt-size-300); + &__omega--xs, + &__end--xs { + &.d-btn--icon-only { --button-padding-x: var(--dt-size-300); } } - &__omega--sm.d-btn--icon-only { - --button-padding-x: var(--dt-size-300); + &__omega--sm, + &__end--sm { + &.d-btn--icon-only { --button-padding-x: var(--dt-size-300); } } - &__omega--md.d-btn--icon-only { - --button-padding-x: var(--dt-size-350); + &__omega--md, + &__end--md { + &.d-btn--icon-only { --button-padding-x: var(--dt-size-350); } } - &__omega--lg.d-btn--icon-only { - --button-padding-x: var(--dt-size-400); + &__omega--lg, + &__end--lg { + &.d-btn--icon-only { --button-padding-x: var(--dt-size-400); } } - &__omega--xl.d-btn--icon-only { - --button-padding-x: var(--dt-size-450); + &__omega--xl, + &__end--xl { + &.d-btn--icon-only { --button-padding-x: var(--dt-size-450); } } } diff --git a/packages/dialtone-vue/components/split_button/split_button-omega.vue b/packages/dialtone-vue/components/split_button/split_button-end.vue similarity index 94% rename from packages/dialtone-vue/components/split_button/split_button-omega.vue rename to packages/dialtone-vue/components/split_button/split_button-end.vue index fa9bea8079..ec59357cdd 100644 --- a/packages/dialtone-vue/components/split_button/split_button-omega.vue +++ b/packages/dialtone-vue/components/split_button/split_button-end.vue @@ -2,10 +2,10 @@ { wrapper = mount(DtSplitButton, { @@ -40,8 +40,8 @@ describe('DtSplitButton Tests', function () { }, plugins: [DtTooltipDirective], components: { - SplitButtonAlpha, - SplitButtonOmega, + SplitButtonStart, + SplitButtonEnd, DtIconSend, }, }, @@ -49,10 +49,10 @@ describe('DtSplitButton Tests', function () { attachTo: document.body, }); - alphaButton = wrapper.find('[data-qa="dt-split-button-alpha"]'); - alphaIconSlot = alphaButton.find('[data-qa="dt-button-icon"]'); - omegaButton = wrapper.find('[data-qa="dt-split-button-omega"]'); - omegaIconSlot = omegaButton.find('[data-qa="dt-button-icon"]'); + startButton = wrapper.find('[data-qa="dt-split-button-start"]'); + startIconSlot = startButton.find('[data-qa="dt-button-icon"]'); + endButton = wrapper.find('[data-qa="dt-split-button-end"]'); + endIconSlot = endButton.find('[data-qa="dt-button-icon"]'); }; beforeAll(() => { @@ -83,15 +83,15 @@ describe('DtSplitButton Tests', function () { describe('When rendered with default props', () => { it('Should render the component', () => { expect(wrapper.exists()).toBe(true); - expect(alphaButton.exists()).toBe(true); - expect(omegaButton.exists()).toBe(true); - expect(omegaIconSlot.exists()).toBe(true); + expect(startButton.exists()).toBe(true); + expect(endButton.exists()).toBe(true); + expect(endIconSlot.exists()).toBe(true); }); it('Should render primary by default', async () => { // Default (no props) button should be d-btn--primary - expect(alphaButton.classes().includes('d-btn--primary')).toBe(true); - expect(omegaButton.classes().includes('d-btn--primary')).toBe(true); + expect(startButton.classes().includes('d-btn--primary')).toBe(true); + expect(endButton.classes().includes('d-btn--primary')).toBe(true); }); }); @@ -101,8 +101,8 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(alphaButton.classes().includes('d-btn--danger')).toBe(true); - expect(omegaButton.classes().includes('d-btn--danger')).toBe(true); + expect(startButton.classes().includes('d-btn--danger')).toBe(true); + expect(endButton.classes().includes('d-btn--danger')).toBe(true); }); }); @@ -112,8 +112,8 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(alphaButton.classes().includes('d-btn--outlined')).toBe(true); - expect(omegaButton.classes().includes('d-btn--outlined')).toBe(true); + expect(startButton.classes().includes('d-btn--outlined')).toBe(true); + expect(endButton.classes().includes('d-btn--outlined')).toBe(true); }); }); @@ -123,7 +123,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(alphaButton.classes().includes('d-btn--loading')).toBe(true); + expect(startButton.classes().includes('d-btn--loading')).toBe(true); }); }); @@ -133,8 +133,8 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(alphaButton.attributes('disabled')).toBeDefined(); - expect(omegaButton.attributes('disabled')).toBeUndefined(); + expect(startButton.attributes('disabled')).toBeDefined(); + expect(endButton.attributes('disabled')).toBeUndefined(); }); }); @@ -144,8 +144,8 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(alphaButton.attributes('disabled')).toBeUndefined(); - expect(omegaButton.attributes('disabled')).toBeDefined(); + expect(startButton.attributes('disabled')).toBeUndefined(); + expect(endButton.attributes('disabled')).toBeDefined(); }); }); @@ -155,8 +155,8 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(alphaButton.attributes('disabled')).toBeDefined(); - expect(omegaButton.attributes('disabled')).toBeDefined(); + expect(startButton.attributes('disabled')).toBeDefined(); + expect(endButton.attributes('disabled')).toBeDefined(); }); }); @@ -166,7 +166,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(alphaButton.classes().includes('d-btn--active')).toBe(true); + expect(startButton.classes().includes('d-btn--active')).toBe(true); }); }); @@ -176,7 +176,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(omegaButton.classes().includes('d-btn--active')).toBe(true); + expect(endButton.classes().includes('d-btn--active')).toBe(true); }); }); @@ -186,8 +186,8 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(alphaButton.classes().includes('d-btn--xl')).toBe(true); - expect(omegaButton.classes().includes('d-btn--xl')).toBe(true); + expect(startButton.classes().includes('d-btn--xl')).toBe(true); + expect(endButton.classes().includes('d-btn--xl')).toBe(true); }); }); @@ -199,11 +199,11 @@ describe('DtSplitButton Tests', function () { }); it('Should render the custom icon', () => { - expect(alphaIconSlot.findComponent(DtIconSend).classes().includes('d-icon--send')).toBe(true); + expect(startIconSlot.findComponent(DtIconSend).classes().includes('d-icon--send')).toBe(true); }); it('Should render left by default', () => { - expect(alphaIconSlot.classes().includes('d-btn__icon--left')).toBe(true); + expect(startIconSlot.classes().includes('d-btn__icon--left')).toBe(true); }); describe('When startIconPosition is set to right', () => { @@ -212,7 +212,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(alphaIconSlot.classes().includes('d-btn__icon--right')).toBe(true); + expect(startIconSlot.classes().includes('d-btn__icon--right')).toBe(true); }); }); }); @@ -225,14 +225,14 @@ describe('DtSplitButton Tests', function () { }); it('Should render start icon in the alpha button at start position', () => { - const startIconSlot = alphaButton.find('[data-qa="dt-button-start-icon"]'); + const startIconSlot = startButton.find('[data-qa="dt-button-start-icon"]'); expect(startIconSlot.exists()).toBe(true); expect(startIconSlot.findComponent(DtIconSend).exists()).toBe(true); }); it('Should render end icon in the alpha button at end position', () => { - const endIconSlot = alphaButton.find('[data-qa="dt-button-end-icon"]'); + const endIconSlot = startButton.find('[data-qa="dt-button-end-icon"]'); expect(endIconSlot.exists()).toBe(true); expect(endIconSlot.findComponent(DtIconSend).exists()).toBe(true); @@ -247,7 +247,7 @@ describe('DtSplitButton Tests', function () { }); it('should render the custom icon', () => { - expect(omegaIconSlot.findComponent(DtIconSend).classes().includes('d-icon--send')).toBe(true); + expect(endIconSlot.findComponent(DtIconSend).classes().includes('d-icon--send')).toBe(true); }); }); @@ -256,7 +256,7 @@ describe('DtSplitButton Tests', function () { mockProps = { startTooltipText: MOCK_START_TOOLTIP_TEXT }; await updateWrapper(); await flushPromises(); - await alphaButton.trigger('mouseenter'); + await startButton.trigger('mouseenter'); const tooltip = document.body.querySelector('[data-qa="dt-tooltip"]'); @@ -269,7 +269,7 @@ describe('DtSplitButton Tests', function () { mockProps = { endTooltipText: MOCK_END_TOOLTIP_TEXT }; await updateWrapper(); await flushPromises(); - await omegaButton.trigger('mouseenter'); + await endButton.trigger('mouseenter'); const tooltip = document.body.querySelector('[data-qa="dt-tooltip"]'); @@ -285,7 +285,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - await alphaButton.trigger('click'); + await startButton.trigger('click'); }); it('Should call listener', async () => { @@ -307,7 +307,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - await omegaButton.trigger('click'); + await endButton.trigger('click'); }); it('Should call listener', async () => { @@ -331,7 +331,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(alphaButton.classes().includes('d-btn--active')).toBe(true); + expect(startButton.classes().includes('d-btn--active')).toBe(true); }); }); @@ -341,7 +341,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(omegaButton.classes().includes('d-btn--active')).toBe(true); + expect(endButton.classes().includes('d-btn--active')).toBe(true); }); }); @@ -351,7 +351,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(alphaButton.classes().includes('d-btn--loading')).toBe(true); + expect(startButton.classes().includes('d-btn--loading')).toBe(true); }); }); @@ -361,8 +361,8 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(alphaButton.attributes('disabled')).toBeDefined(); - expect(omegaButton.attributes('disabled')).toBeUndefined(); + expect(startButton.attributes('disabled')).toBeDefined(); + expect(endButton.attributes('disabled')).toBeUndefined(); }); }); @@ -372,8 +372,8 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(alphaButton.attributes('disabled')).toBeUndefined(); - expect(omegaButton.attributes('disabled')).toBeDefined(); + expect(startButton.attributes('disabled')).toBeUndefined(); + expect(endButton.attributes('disabled')).toBeDefined(); }); }); @@ -383,7 +383,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(alphaIconSlot.findComponent(DtIconSend).classes().includes('d-icon--send')).toBe(true); + expect(startIconSlot.findComponent(DtIconSend).classes().includes('d-icon--send')).toBe(true); }); }); @@ -393,7 +393,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(omegaIconSlot.findComponent(DtIconSend).classes().includes('d-icon--send')).toBe(true); + expect(endIconSlot.findComponent(DtIconSend).classes().includes('d-icon--send')).toBe(true); }); }); @@ -403,9 +403,9 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - const alphaComponent = wrapper.findComponent(SplitButtonAlpha); + const startComponent = wrapper.findComponent(SplitButtonStart); - expect(alphaComponent.props('tooltipText')).toBe(MOCK_START_TOOLTIP_TEXT); + expect(startComponent.props('tooltipText')).toBe(MOCK_START_TOOLTIP_TEXT); }); }); @@ -415,9 +415,9 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - const omegaComponent = wrapper.findComponent(SplitButtonOmega); + const endComponent = wrapper.findComponent(SplitButtonEnd); - expect(omegaComponent.props('tooltipText')).toBe(MOCK_END_TOOLTIP_TEXT); + expect(endComponent.props('tooltipText')).toBe(MOCK_END_TOOLTIP_TEXT); }); }); @@ -427,7 +427,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(alphaButton.attributes('aria-label')).toBe('Call action'); + expect(startButton.attributes('aria-label')).toBe('Call action'); }); }); @@ -437,7 +437,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(omegaButton.attributes('aria-label')).toBe('More options'); + expect(endButton.attributes('aria-label')).toBe('More options'); }); }); @@ -448,7 +448,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(alphaIconSlot.classes().includes('d-btn__icon--right')).toBe(true); + expect(startIconSlot.classes().includes('d-btn__icon--right')).toBe(true); }); }); @@ -458,7 +458,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - const labelEl = alphaButton.find('[data-qa="dt-button-label"]'); + const labelEl = startButton.find('[data-qa="dt-button-label"]'); expect(labelEl.classes().includes('custom-label-class')).toBe(true); }); @@ -473,7 +473,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(alphaIconSlot.findComponent(DtIconSend).exists()).toBe(true); + expect(startIconSlot.findComponent(DtIconSend).exists()).toBe(true); expect(wrapper.find('[data-qa="old-alpha-icon"]').exists()).toBe(false); }); }); @@ -487,7 +487,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(omegaIconSlot.findComponent(DtIconSend).exists()).toBe(true); + expect(endIconSlot.findComponent(DtIconSend).exists()).toBe(true); expect(wrapper.find('[data-qa="old-omega-icon"]').exists()).toBe(false); }); }); @@ -505,7 +505,7 @@ describe('DtSplitButton Tests', function () { global: { stubs: { transition: false }, plugins: [DtTooltipDirective], - components: { SplitButtonAlpha, SplitButtonOmega, DtIconSend }, + components: { SplitButtonStart, SplitButtonEnd, DtIconSend }, }, attrs: { ...baseAttrs, ...mockAttrs }, attachTo: document.body, @@ -526,7 +526,7 @@ describe('DtSplitButton Tests', function () { global: { stubs: { transition: false }, plugins: [DtTooltipDirective], - components: { SplitButtonAlpha, SplitButtonOmega, DtIconSend }, + components: { SplitButtonStart, SplitButtonEnd, DtIconSend }, }, attrs: { ...baseAttrs, ...mockAttrs }, attachTo: document.body, @@ -541,7 +541,7 @@ describe('DtSplitButton Tests', function () { describe('When start button is clicked', () => { it('Should emit deprecated alpha-clicked event', async () => { - await alphaButton.trigger('click'); + await startButton.trigger('click'); expect(wrapper.emitted()).toHaveProperty('alpha-clicked'); }); @@ -549,7 +549,7 @@ describe('DtSplitButton Tests', function () { describe('When end button is clicked', () => { it('Should emit deprecated omega-clicked event', async () => { - await omegaButton.trigger('click'); + await endButton.trigger('click'); expect(wrapper.emitted()).toHaveProperty('omega-clicked'); }); @@ -563,9 +563,9 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - const alphaComponent = wrapper.findComponent(SplitButtonAlpha); + const startComponent = wrapper.findComponent(SplitButtonStart); - expect(alphaComponent.props('to')).toBe('/some-route'); + expect(startComponent.props('to')).toBe('/some-route'); }); }); @@ -576,9 +576,9 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - const alphaComponent = wrapper.findComponent(SplitButtonAlpha); + const startComponent = wrapper.findComponent(SplitButtonStart); - expect(alphaComponent.props('to')).toEqual(route); + expect(startComponent.props('to')).toEqual(route); }); }); @@ -588,9 +588,9 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - const alphaComponent = wrapper.findComponent(SplitButtonAlpha); + const startComponent = wrapper.findComponent(SplitButtonStart); - expect(alphaComponent.props('href')).toBe('https://example.com'); + expect(startComponent.props('href')).toBe('https://example.com'); }); }); @@ -604,10 +604,10 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - const alphaComponent = wrapper.findComponent(SplitButtonAlpha); + const startComponent = wrapper.findComponent(SplitButtonStart); - expect(alphaComponent.props('target')).toBe('_blank'); - expect(alphaComponent.props('rel')).toBe('noopener noreferrer'); + expect(startComponent.props('target')).toBe('_blank'); + expect(startComponent.props('rel')).toBe('noopener noreferrer'); }); }); @@ -617,9 +617,9 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - const alphaComponent = wrapper.findComponent(SplitButtonAlpha); + const startComponent = wrapper.findComponent(SplitButtonStart); - expect(alphaComponent.props('replace')).toBe(true); + expect(startComponent.props('replace')).toBe(true); }); }); }); @@ -642,7 +642,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - const leading = alphaButton.find('.d-btn__leading'); + const leading = startButton.find('.d-btn__leading'); expect(leading.exists()).toBe(true); expect(leading.classes()).toContain('custom-leading'); @@ -656,7 +656,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - const trailing = alphaButton.find('.d-btn__trailing'); + const trailing = startButton.find('.d-btn__trailing'); expect(trailing.exists()).toBe(true); expect(trailing.classes()).toContain('custom-trailing'); @@ -669,7 +669,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(alphaButton.find('[data-qa="test-leading"]').exists()).toBe(true); + expect(startButton.find('[data-qa="test-leading"]').exists()).toBe(true); }); }); @@ -679,7 +679,7 @@ describe('DtSplitButton Tests', function () { updateWrapper(); - expect(alphaButton.find('[data-qa="test-trailing"]').exists()).toBe(true); + expect(startButton.find('[data-qa="test-trailing"]').exists()).toBe(true); }); }); }); diff --git a/packages/dialtone-vue/components/split_button/split_button.vue b/packages/dialtone-vue/components/split_button/split_button.vue index 4255aebfb8..5905ecd261 100644 --- a/packages/dialtone-vue/components/split_button/split_button.vue +++ b/packages/dialtone-vue/components/split_button/split_button.vue @@ -4,9 +4,9 @@ :class="[rootClass, 'd-split-btn']" :style="{ width }" > - @@ -71,7 +71,7 @@ - + - - + @@ -149,8 +149,8 @@ import { BUTTON_SIZE_MODIFIERS, ICON_POSITION_MODIFIERS, } from '@/components/button'; -import SplitButtonAlpha from './split_button-alpha.vue'; -import SplitButtonOmega from './split_button-omega.vue'; +import SplitButtonStart from './split_button-start.vue'; +import SplitButtonEnd from './split_button-end.vue'; import { DtDropdown } from '@/components/dropdown'; import { hasSlotContent, warnIfUnmounted, returnFirstEl } from '@/common/utils'; @@ -159,9 +159,9 @@ export default { name: 'DtSplitButton', components: { - SplitButtonOmega, + SplitButtonEnd, DtDropdown, - SplitButtonAlpha, + SplitButtonStart, }, inheritAttrs: false, @@ -581,7 +581,7 @@ export default { }, computed: { - alphaButtonProps () { + startButtonProps () { return { active: this.alphaActive ?? this.startActive, ariaLabel: this.alphaAriaLabel ?? this.startAriaLabel, @@ -606,7 +606,7 @@ export default { }; }, - omegaButtonProps () { + endButtonProps () { return { id: this.omegaId ?? this.endId, active: this.omegaActive ?? this.endActive, @@ -646,11 +646,11 @@ export default { }, validateProps () { - this.validateAlphaButtonProps(); - this.validateOmegaButtonProps(); + this.validateStartButtonProps(); + this.validateEndButtonProps(); }, - validateAlphaButtonProps () { + validateStartButtonProps () { if (hasSlotContent(this.$slots.default)) return; if ((hasSlotContent(this.$slots.startIcon) || hasSlotContent(this.$slots.alphaIcon)) && @@ -659,7 +659,7 @@ export default { } }, - validateOmegaButtonProps () { + validateEndButtonProps () { if (hasSlotContent(this.$slots.end) || hasSlotContent(this.$slots.omega)) return; if (!(this.omegaTooltipText ?? this.endTooltipText)) { From a15ad42f7b3f6dac8a2a1648714de3b391f7e428 Mon Sep 17 00:00:00 2001 From: francisrupert Date: Sat, 7 Mar 2026 16:50:47 -0600 Subject: [PATCH 17/47] update test descriptions to use 'start' instead of 'alpha' terminology --- .../split_button/split_button.test.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/dialtone-vue/components/split_button/split_button.test.js b/packages/dialtone-vue/components/split_button/split_button.test.js index f1f23a03e8..a5a4b3b60d 100644 --- a/packages/dialtone-vue/components/split_button/split_button.test.js +++ b/packages/dialtone-vue/components/split_button/split_button.test.js @@ -224,14 +224,14 @@ describe('DtSplitButton Tests', function () { updateWrapper(); }); - it('Should render start icon in the alpha button at start position', () => { + it('Should render start icon in the start button at start position', () => { const startIconSlot = startButton.find('[data-qa="dt-button-start-icon"]'); expect(startIconSlot.exists()).toBe(true); expect(startIconSlot.findComponent(DtIconSend).exists()).toBe(true); }); - it('Should render end icon in the alpha button at end position', () => { + it('Should render end icon in the start button at end position', () => { const endIconSlot = startButton.find('[data-qa="dt-button-end-icon"]'); expect(endIconSlot.exists()).toBe(true); @@ -558,7 +558,7 @@ describe('DtSplitButton Tests', function () { describe('Navigation Tests', () => { describe('When startTo is provided', () => { - it('Should forward to prop to the alpha DtButton', () => { + it('Should forward to prop to the start DtButton', () => { mockProps = { startTo: '/some-route' }; updateWrapper(); @@ -570,7 +570,7 @@ describe('DtSplitButton Tests', function () { }); describe('When startTo is an object', () => { - it('Should forward the route object to the alpha DtButton', () => { + it('Should forward the route object to the start DtButton', () => { const route = { name: 'home', params: { id: 1 } }; mockProps = { startTo: route }; @@ -583,7 +583,7 @@ describe('DtSplitButton Tests', function () { }); describe('When startHref is provided', () => { - it('Should forward href prop to the alpha DtButton', () => { + it('Should forward href prop to the start DtButton', () => { mockProps = { startHref: 'https://example.com' }; updateWrapper(); @@ -595,7 +595,7 @@ describe('DtSplitButton Tests', function () { }); describe('When startTarget and startRel are provided', () => { - it('Should forward target and rel props to the alpha DtButton', () => { + it('Should forward target and rel props to the start DtButton', () => { mockProps = { startHref: 'https://example.com', startTarget: '_blank', @@ -612,7 +612,7 @@ describe('DtSplitButton Tests', function () { }); describe('When startReplace is provided', () => { - it('Should forward replace prop to the alpha DtButton', () => { + it('Should forward replace prop to the start DtButton', () => { mockProps = { startTo: '/some-route', startReplace: true }; updateWrapper(); @@ -664,7 +664,7 @@ describe('DtSplitButton Tests', function () { }); describe('When leading slot is provided', () => { - it('should render leading content through to alpha button', () => { + it('should render leading content through to start button', () => { mockSlots = { leading: 'L' }; updateWrapper(); @@ -674,7 +674,7 @@ describe('DtSplitButton Tests', function () { }); describe('When trailing slot is provided', () => { - it('should render trailing content through to alpha button', () => { + it('should render trailing content through to start button', () => { mockSlots = { trailing: 'T' }; updateWrapper(); From eccff2030ccb71b2896f17f08c926d63db1fc20a Mon Sep 17 00:00:00 2001 From: francisrupert Date: Sat, 7 Mar 2026 21:52:26 -0600 Subject: [PATCH 18/47] init mode directive --- .../docs/components/mode-island.md | 350 +++++++++++------- .../docs/guides/theme-and-mode/index.md | 15 +- .../build/less/components/mode-island.less | 5 +- .../directives/mode_directive/index.js | 1 + .../directives/mode_directive/mode.js | 117 ++++++ .../directives/mode_directive/mode.mdx | 43 +++ .../directives/mode_directive/mode.stories.js | 28 ++ .../directives/mode_directive/mode.test.js | 256 +++++++++++++ .../mode_directive_default.story.vue | 186 ++++++++++ packages/dialtone-vue/index.js | 1 + 10 files changed, 873 insertions(+), 129 deletions(-) create mode 100644 packages/dialtone-vue/directives/mode_directive/index.js create mode 100644 packages/dialtone-vue/directives/mode_directive/mode.js create mode 100644 packages/dialtone-vue/directives/mode_directive/mode.mdx create mode 100644 packages/dialtone-vue/directives/mode_directive/mode.stories.js create mode 100644 packages/dialtone-vue/directives/mode_directive/mode.test.js create mode 100644 packages/dialtone-vue/directives/mode_directive/mode_directive_default.story.vue diff --git a/apps/dialtone-documentation/docs/components/mode-island.md b/apps/dialtone-documentation/docs/components/mode-island.md index 595f8874b7..4f7551a864 100644 --- a/apps/dialtone-documentation/docs/components/mode-island.md +++ b/apps/dialtone-documentation/docs/components/mode-island.md @@ -1,8 +1,8 @@ --- title: Mode Island -description: Create independent sections with their own color modes. +description: Apply light, dark, or inverted color mode to any element or region. status: beta -keywords: ["theme island","mode override","d-mode-island","DtModeIsland","dt-mode-island"] +keywords: ["theme island","mode override","v-dt-mode","directive","light","dark","inverted"] --- @@ -17,17 +17,30 @@ keywords: ["theme island","mode override","d-mode-island","DtModeIsland","dt-mod - Mode: {{ currentMode.charAt(0).toUpperCase() + currentMode.slice(1) }} - Contrast: {{ currentContrast.charAt(0).toUpperCase() + currentContrast.slice(1) }} + + Mode: + {{ currentMode.charAt(0).toUpperCase() + currentMode.slice(1) }} + + + Contrast: + {{ currentContrast.charAt(0).toUpperCase() + currentContrast.slice(1) }} + - @@ -446,9 +504,9 @@ showHtmlWarning /> Light @@ -456,9 +514,9 @@ showHtmlWarning /> Dark @@ -481,9 +539,9 @@ showHtmlWarning /> Inverted @@ -491,9 +549,9 @@ showHtmlWarning /> Light @@ -501,9 +559,9 @@ showHtmlWarning /> Dark @@ -542,7 +600,7 @@ showHtmlWarning /> @@ -565,7 +623,7 @@ showHtmlWarning /> @@ -588,7 +646,7 @@ showHtmlWarning /> @@ -614,9 +672,9 @@ vueCode=' Inverted @@ -625,9 +683,9 @@ vueCode=' Inverted @@ -641,7 +699,7 @@ vueCode=' - - - - - - - - - ' showHtmlWarning /> @@ -971,32 +908,6 @@ The following styles are available as a circle shape. - - - - - - - - - - - @@ -1067,30 +978,6 @@ vueCode=' /> - - - - - - - - - ' showHtmlWarning /> diff --git a/apps/dialtone-documentation/docs/components/keyboard-shortcut.md b/apps/dialtone-documentation/docs/components/keyboard-shortcut.md index 48390c23e8..ad100b8fab 100644 --- a/apps/dialtone-documentation/docs/components/keyboard-shortcut.md +++ b/apps/dialtone-documentation/docs/components/keyboard-shortcut.md @@ -40,23 +40,24 @@ vueCode=' ### Inverted - The inverted prop has been deprecated in favor of using DtModeIsland as a wrapper. + The inverted prop has been deprecated. Use the + v-dt-mode directive + instead, or DtModeIsland + when no natural container element exists. -In place of the inverted prop, use the DtModeIsland component as a wrapper. +In place of the inverted prop, use the v-dt-mode directive on the component element. - - - +
+ +
diff --git a/apps/dialtone-documentation/docs/components/link.md b/apps/dialtone-documentation/docs/components/link.md index 62541cd094..65a86ec494 100644 --- a/apps/dialtone-documentation/docs/components/link.md +++ b/apps/dialtone-documentation/docs/components/link.md @@ -95,30 +95,29 @@ showHtmlWarning /> kind="error" class="d-wmx100p d-my16" > - The inverted prop has been deprecated in favor of using DtModeIsland as a wrapper. + The inverted prop has been deprecated. Use the + v-dt-mode directive + instead, or DtModeIsland + when no natural container element exists. -In place of the inverted prop, use the DtModeIsland component as a wrapper. +In place of the inverted prop, use the v-dt-mode directive on the component element. - - - Base link - Danger link - Success link - Warning link - Muted link - Mention link - - + + Base link + Danger link + Success link + Warning link + Muted link + Mention link + diff --git a/apps/dialtone-documentation/docs/components/table.md b/apps/dialtone-documentation/docs/components/table.md index 3a19cfa337..742f52aba4 100644 --- a/apps/dialtone-documentation/docs/components/table.md +++ b/apps/dialtone-documentation/docs/components/table.md @@ -83,14 +83,14 @@ keywords: ["data table", "grid", "rows", "d-table", "DtTable", "dt-table", "data ### Inverted Style - The d-table--inverted modifier has been deprecated in favor of using DtModeIsland as a wrapper. + The d-table--inverted modifier has been deprecated. Use the v-dt-mode directive instead, or DtModeIsland when no natural container element exists. -In place of the d-table--inverted modifier, wrap the table in the DtModeIsland component. +In place of the d-table--inverted modifier, use the v-dt-mode directive on the table element. - - +
+
@@ -109,13 +109,11 @@ In place of the d-table--inverted modifier, wrap the table in the <
Office List
-
+
```html - - ...
-
+...
``` ### Striped diff --git a/apps/dialtone-documentation/docs/components/tabs.md b/apps/dialtone-documentation/docs/components/tabs.md index 2b7fd6cb93..129fdc93c1 100644 --- a/apps/dialtone-documentation/docs/components/tabs.md +++ b/apps/dialtone-documentation/docs/components/tabs.md @@ -176,28 +176,28 @@ showHtmlWarning /> ### Inverted - The inverted prop has been deprecated in favor of using DtModeIsland as a wrapper. + The inverted prop has been deprecated. Use the v-dt-mode directive instead, or DtModeIsland when no natural container element exists. -In place of the inverted prop, use the DtModeIsland component as a wrapper. +In place of the inverted prop, use the v-dt-mode directive on the component element. - - - +
+
+ +
+
diff --git a/apps/dialtone-documentation/docs/components/tooltip.md b/apps/dialtone-documentation/docs/components/tooltip.md index d8d041ac40..d6b1d947c2 100644 --- a/apps/dialtone-documentation/docs/components/tooltip.md +++ b/apps/dialtone-documentation/docs/components/tooltip.md @@ -135,6 +135,10 @@ showHtmlWarning /> ### Inverted + + The inverted prop is still required for DtTooltip because the tooltip renders outside the normal DOM tree (via Tippy.js). The v-dt-mode directive cannot reach the tooltip shell. See DLT-3077 for follow-up. + +