Directive for scoping light, dark, or inverted color mode.
Use v-dt-mode by default on the element that defines the region. It adds no wrapper and does not paint a background. Choose DtModeIsland when you deliberately want its container and automatic primary surface together, or cannot use the directive.
Both APIs scope token-based styles to the specified color mode. Putting the directive on an existing element preserves layout relationships, such as direct children of a flex or grid container.
Use v-dt-mode by default for new mode-scoping code.
| Situation | Recommendation |
|---|---|
| The region already has a container (section, card, toolbar, or button) | v-dt-mode |
| You need a new container, including a painted surface | Usually a native container with v-dt-mode; add d-bgc-primary when needed |
| You deliberately want the component's container and automatic primary surface together | DtModeIsland is a reasonable convenience |
| You cannot install the currently exported directive plugin | DtModeIsland |
| Explicit light/dark attributes must appear in server-rendered HTML | Consider DtModeIsland; verify rendering and hydration in your setup |
| Positioned overlay content | The positioned component's content-mode prop |
The current directive applies attributes after mounting; the component binds them in its template. This makes the component worth considering for explicit light/dark server markup. It does not guarantee correct inverted mode, root-derived contrast/material, or hydration in every SSR setup.
The directive and content-mode prop use invert; the component uses mode="inverted".
<dt-text v-dt-mode:dark tone="positive"> Dark content </dt-text> <dt-text v-dt-mode:light tone="positive"> Light content </dt-text> <dt-text v-dt-mode:invert tone="positive"> Inverted — opposite of parent or root </dt-text>
This effectively removes the need for inverted props or variants on elements or components.
For example, instead of using inverted on a DtButton, use v-dt-mode:invert
<dt-button>Button</dt-button> <dt-button v-dt-mode:invert>Button</dt-button>
Bind a reactive variable as the directive arg to switch modes at runtime.
<dt-text v-dt-mode:{mode} align="center" tone="positive"> ... mode </dt-text>
Pass a boolean value to conditionally apply or remove the directive. When false, mode attributes are removed entirely.
<dt-button v-dt-mode:invert="isInverted">Button</dt-button>
[data-dt-mode="light"] and [data-dt-mode="dark"] attribute selectors[data-dt-mode][data-dt-contrast="high"]<html> element and kept in sync via MutationObserver<html> element the same way — data-dt-material propagates onto the island/directive element so --dt-color-black-* re-binds correctly inside inverted regionsinvert mode, the directive reads the nearest ancestor's data-dt-mode, computes the opposite, and reacts when it changesdata-dt-brand (theme) and data-dt-material at the root; do not set local overrides on mode boundaries. Material is paired to brand for visual coherence (see brand-locked materials).The default mode — inverts relative to the nearest parent mode boundary or the root. When no arg is provided, v-dt-mode defaults to invert.
<section v-dt-mode class="d-p-200 d-bar-400"> <dt-text as="p" tone="positive">Inverted mode (opposite of parent)</dt-text> </section>
Explicitly set to light mode regardless of parent or root mode.
<section v-dt-mode:light class="d-p-200 d-bar-400"> <dt-text as="p" tone="positive">Always light mode</dt-text> </section>
Explicitly set to dark mode regardless of parent or root mode.
<section v-dt-mode:dark class="d-p-200 d-bar-400"> <dt-text as="p" tone="positive">Always dark mode</dt-text> </section>
Mode boundaries can be nested. Each v-dt-mode:invert reads the nearest parent boundary and flips. In this example the first level is explicitly set to light mode, the second level inverts against that, and the third level inverts again.
<dt-stack gap="200" v-dt-mode:light class="d-p-200 d-bar-400 d-bgc-secondary d-ba"> <dt-text as="p" tone="positive" text-box-trim="both">Explicit Light</dt-text> <dt-stack v-dt-mode gap="200" class="d-p-200 d-bar-400 d-bgc-secondary"> <dt-text as="p" tone="positive" text-box-trim="both">Inverted (Dark)</dt-text> <dt-stack v-dt-mode gap="200" class="d-p-200 d-bar-300 d-bgc-secondary"> <dt-text as="p" tone="positive" text-box-trim="both">Inverted again (Light)</dt-text> </dt-stack> </dt-stack> </dt-stack>
The directive scopes color mode without supplying a background. Keep the region's existing background styling, or choose a surface explicitly with d-bgc-primary or the surface prop when using DtBox. For example, <dt-box v-dt-mode:invert surface="primary"> scopes the mode and paints its primary surface. Check foreground and background contrast in both root modes.
The component's d-mode-island class automatically paints var(--dt-color-surface-primary) from the island's active mode. A background utility can override that surface.
<section v-dt-mode:invert> <dt-text as="p">The region keeps its existing background styling.</dt-text> </section>
These live examples choose backgrounds explicitly with the directive, or use the component's automatic surface:
<section v-dt-mode:invert class="d-bgc-primary d-p-200 d-bar-400 d-w100p"> <dt-text as="p">The region explicitly uses the active mode's primary surface.</dt-text> </section> <dt-box v-dt-mode:invert surface="secondary" padding="200" border-radius="400" class="d-w100p"> <dt-text as="p">DtBox: explicit secondary surface, inverted mode</dt-text> </dt-box> <dt-mode-island class="d-p-200 d-bar-400 d-w100p"> <dt-stack gap="200"> <dt-text as="p" kind="code" :size="100" tone="tertiary">Component: automatic primary surface, inverted mode</dt-text> <div> <dt-button>Button</dt-button> </div> </dt-stack> </dt-mode-island> <section v-dt-mode:dark class="d-p-200 d-bar-400 d-w100p d-bgc-critical"> <dt-stack gap="200"> <dt-text as="p" kind="code" :size="100" tone="tertiary">Explicit critical background, dark mode directive</dt-text> <div> <dt-button>Button</dt-button> </div> </dt-stack> </section> <section v-dt-mode:light class="d-p-200 d-bar-400 d-w100p d-bgc-critical"> <dt-stack gap="200"> <dt-text as="p" kind="code" :size="100" tone="tertiary">Explicit critical background, light mode directive</dt-text> <div> <dt-button>Button</dt-button> </div> </dt-stack> </section>
A real-world pattern: the callbar container already exists as a semantic element. The directive applies mode theming directly — no wrapper needed.
<dt-stack v-dt-mode class="d-ba d-bc-subtle d-bgc-secondary d-p-100 d-pis-125 d-py-100 d-bar-450 d-bs-md d-w100p" direction="row" gap="400"> <dt-stack gap="100" direction="row"> <dt-avatar full-name="TA" seed="ted-anderson" :size="400" /> <dt-stack gap="25"> <dt-text kind="label" :size="300" density="200">Ted Anderson</dt-text> <dt-stack direction="row" gap="50" align="baseline"> <dt-text kind="body" :size="100" tone="tertiary" wrap="nowrap" numeric>(913) 555-6745</dt-text> <dt-text kind="body" :size="100" tone="muted">•</dt-text> <dt-text kind="body" :size="100" tone="tertiary" numeric>21:18</dt-text> </dt-stack> </dt-stack> </dt-stack> <dt-stack class="d-fl1" direction="row" gap="25" justify="center"> <dt-button class="d-px-100 d-w-100" :size="100" kind="critical"> <template #blockStartIcon> <dt-icon name="mic" size="300" /> </template> Unmute </dt-button> <dt-button class="d-px-100 d-w-100" :size="100" kind="muted" importance="clear"> <template #blockStartIcon> <dt-icon name="record-filled" size="300" /> </template> Record </dt-button> <dt-button class="d-px-100 d-w-100" :size="100" kind="muted" importance="clear"> <template #blockStartIcon> <dt-icon name="keypad" size="300" /> </template> Keypad </dt-button> <dt-button class="d-px-100 d-w-100" :size="100" kind="muted" importance="clear"> <template #blockStartIcon> <dt-icon name="user-plus" size="300" /> </template> Add </dt-button> <dt-button class="d-px-100 d-w-100" :size="100" kind="muted" importance="clear"> <template #blockStartIcon> <dt-icon name="more-horizontal" size="300" /> </template> More </dt-button> </dt-stack> <dt-stack> <dt-button class="d-p-150" :size="400" kind="critical"> <template #startIcon> <dt-icon name="phone-hang-up" size="500" /> </template> </dt-button> </dt-stack> </dt-stack> <dt-text as="p" kind="label" :size="200" tone="muted" class="d-mbs-100">* Not real, still just an example</dt-text>
Popovers, Dropdowns, Modals, and Hovercards render their content outside the normal DOM tree, so v-dt-mode on the component itself won't reach the positioned element. Use their content-mode prop (contentMode in JavaScript) to apply the mode directly to the positioned content.
<!-- Hovercard --> <dt-hovercard placement="top-start" content-mode="invert"> <template #anchor> <dt-button :size="200" kind="muted" importance="outlined">Default</dt-button> </template> <template #content> <ExampleProfileCard /> </template> </dt-hovercard> <!-- Popover --> <dt-popover content-mode="invert" placement="top-start" dialogClass="d-w-350"> <template #anchor> <dt-button :size="200" kind="muted" importance="outlined"> Inverted </dt-button> </template> <template #content="{ close }"> <dt-text as="p">This Popover content is in the <dt-text strength="strong">inverted</dt-text> mode.</dt-text> </template> </dt-popover> <!-- Dropdown --> <dt-dropdown content-mode="invert" navigation-type="arrow-keys" placement="bottom-start"> <template #anchor="{ attrs }"> <dt-button v-bind="attrs" :size="200" kind="muted" importance="outlined"> Inverted <template #endIcon="{ iconSize }"> <dt-icon name="chevron-down" :size="iconSize" /> </template> </dt-button> </template> <template #list="{ close }"> <dt-list-item v-for="item in items" :key="item.id" role="menuitem" :navigation-type="arrow - keys" @click="close" > {{ item.name }} </dt-list-item> </template> </dt-dropdown>
Both APIs create scoped mode regions and share utility functions, but have separate implementations. The component renders a container, controlled by its as prop, and paints the active mode's primary surface. The directive attaches to an existing element, such as <span v-dt-mode:invert>...</span>, without adding a container or background.
The component's default mode is inverted, equivalent in intent to the directive's invert argument. See Directive or component? for registration and server-rendering considerations.
<dt-mode-island as="section"> Rendered as a section element inverted </dt-mode-island> <dt-mode-island> Inverted (default) </dt-mode-island> <dt-mode-island mode="light"> Light </dt-mode-island> <dt-mode-island mode="dark"> Dark </dt-mode-island>
Changing color mode is visual. The directive preserves the element's semantics; the component's as prop determines its container semantics. Both inherit the root's high-contrast setting. Check readability against the chosen background in light, dark, and high-contrast modes.
import { DtModeDirective } from '@dialpad/dialtone-vue';
app.use(DtModeDirective);
import { DtModeIsland } from '@dialpad/dialtone-vue';
Name
|
Type
|
|---|---|
default | Slot for main content |
Name
|
Default
|
Type
|
|---|---|---|
as | 'div' |
"div"
|
"section"
|
"nav"
|
"article"
|
"aside"
|
"header"
|
"footer"
|
"main"
Set this prop to render the mode island as a specific HTML element. |
mode | inverted |
"inverted"
|
"light"
|
"dark"
The mode to apply to the island. |
Mode documentation last updated Tuesday, October 20, 2026