logo

Themes & CSS Variables

The library ships two classname maps — defaultStyles (light) and darkStyles — and exposes every color as a --sjv-* CSS custom property so you can retheme without swapping the style prop.

Built-in Themes

<script lang="ts">
    import { JsonView, defaultStyles, darkStyles } from '@humanspeak/svelte-json-view-lite'
</script>

<!-- Light (default) -->
<JsonView data={payload} />

<!-- Dark -->
<JsonView data={payload} style={darkStyles} />
<script lang="ts">
    import { JsonView, defaultStyles, darkStyles } from '@humanspeak/svelte-json-view-lite'
</script>

<!-- Light (default) -->
<JsonView data={payload} />

<!-- Dark -->
<JsonView data={payload} style={darkStyles} />

Both exports conform to StyleProps. Spread them to patch individual keys:

<JsonView
    data={payload}
    style={{ ...defaultStyles, quotesForFieldNames: true, stringifyStringValues: true }}
/>
<JsonView
    data={payload}
    style={{ ...defaultStyles, quotesForFieldNames: true, stringifyStringValues: true }}
/>

CSS Variable Tokens

Every color used by the viewer is declared on the container element as a --sjv-* custom property. Overriding any token cascades to every descendant that reads it.

TokenApplies toLight defaultDark default
--sjv-backgroundContainer background#eeergb(0, 43, 54)
--sjv-labelField labels (keys)#000000rgb(253, 246, 227)
--sjv-punctuationBraces, brackets, colons, commas#000000rgb(253, 246, 227)
--sjv-stringString valuesrgb(42, 63, 60)rgb(203, 75, 22)
--sjv-numberNumber values#0b75f5rgb(211, 54, 130)
--sjv-booleanBoolean valuesrgb(70, 144, 56)rgb(174, 129, 255)
--sjv-nullnull values#df113argb(129, 181, 172)
--sjv-undefinedundefined values#df113argb(129, 181, 172)
--sjv-otherbigint, Date, functions#43413drgb(38, 139, 210)
--sjv-expanderArrow glyphs + collapsed summary#000000rgb(253, 246, 227)

Global Overrides

Set tokens on :root to retheme every instance on the page:

:root {
    --sjv-string: lavender;
    --sjv-number: var(--brand-500);
    --sjv-background: transparent;
}
:root {
    --sjv-string: lavender;
    --sjv-number: var(--brand-500);
    --sjv-background: transparent;
}

Because the defaults are declared on the container (not fallbacks on var()), inheritance from :root alone does not win against the per-container declarations. Use a selector with higher specificity, or target the tree element directly:

[role='tree'] {
    --sjv-background: transparent;
}
[role='tree'] {
    --sjv-background: transparent;
}

Per-Instance Overrides

Scope custom tokens to one viewer with a wrapping class. The library’s container selector has specificity (0,1,0); anything with (0,2,0) or higher wins without !important:

<div class="payload-view">
    <JsonView data={payload} />
</div>

<style>
    .payload-view :global([role='tree']) {
        --sjv-background: transparent;
        --sjv-label: var(--color-foreground);
        --sjv-string: #f59e0b;
        --sjv-number: var(--color-brand-500);
    }
</style>
<div class="payload-view">
    <JsonView data={payload} />
</div>

<style>
    .payload-view :global([role='tree']) {
        --sjv-background: transparent;
        --sjv-label: var(--color-foreground);
        --sjv-string: #f59e0b;
        --sjv-number: var(--color-brand-500);
    }
</style>

This is exactly how the landing page’s live playground tracks light and dark modes — it maps --sjv-* tokens to the site’s design-system variables.

Dark Mode with mode-watcher

If you use mode-watcher, derive the style prop from the current mode:

<script lang="ts">
    import { JsonView, defaultStyles, darkStyles } from '@humanspeak/svelte-json-view-lite'
    import { mode } from 'mode-watcher'

    const theme = $derived(mode.current === 'dark' ? darkStyles : defaultStyles)
</script>

<JsonView data={payload} style={theme} />
<script lang="ts">
    import { JsonView, defaultStyles, darkStyles } from '@humanspeak/svelte-json-view-lite'
    import { mode } from 'mode-watcher'

    const theme = $derived(mode.current === 'dark' ? darkStyles : defaultStyles)
</script>

<JsonView data={payload} style={theme} />

Or skip the prop swap entirely and let CSS variables switch based on a root class:

:root { --sjv-background: #eee; }
.dark { --sjv-background: #0f172a; }
:root { --sjv-background: #eee; }
.dark { --sjv-background: #0f172a; }

Boolean Style Options

StyleProps has three booleans that affect rendering regardless of which palette you pick:

OptionEffect
noQuotesForStringValuesOmits the " around string leaves
quotesForFieldNamesWraps field labels in "
stringifyStringValuesPasses string values through JSON.stringify so control characters render as escapes
<JsonView
    data={payload}
    style={{ ...defaultStyles, quotesForFieldNames: true, noQuotesForStringValues: true }}
/>
<JsonView
    data={payload}
    style={{ ...defaultStyles, quotesForFieldNames: true, noQuotesForStringValues: true }}
/>

Related