Skip to content
WovenUI LogoWovenUI

Templates

Dashboard layout template

A dashboard shell with a side navigation, a page header, three stat tiles and a recent orders list. The navigation sits above the content below the md breakpoint.

Preview

Overview

  • Revenue

    $48,210

  • Orders

    1,284

  • Refund rate

    1.8%

Recent orders

  • Lina Haddad$320.00
  • Omar Siddiqui$86.50
  • Grace Okafor$1,140.00
Live preview of the dashboard layout, rendered from the HTML on this page.

HTML to copy

The markup of the preview, in WovenUI core utility classes. It needs only the prebuilt@wovenui/core/dist/wovenui.min.css, or your own build with $generate-utility-classes: true. No components package and no JavaScript.

HTML
<div class="grid cols-12@md bg-base text-regular border rounded-lg overflow-hidden">
    <nav class="col-span-3@md col-span-2@lg bg-alternate p-4" aria-label="Dashboard">
        <p class="font-semibold m-0 mb-3">Northwind</p>
        <ul class="flex col@md gap-1 m-0 p-0 overflow-x-auto text-sm" role="list">
            <li><a class="block whitespace-nowrap bg-selected text-brand font-medium rounded-md px-3 py-2" href="/dashboard/" aria-current="page">Overview</a></li>
            <li><a class="block whitespace-nowrap text-muted hover:bg-hover hover:text-regular rounded-md px-3 py-2" href="/dashboard/orders/">Orders</a></li>
            <li><a class="block whitespace-nowrap text-muted hover:bg-hover hover:text-regular rounded-md px-3 py-2" href="/dashboard/customers/">Customers</a></li>
            <li><a class="block whitespace-nowrap text-muted hover:bg-hover hover:text-regular rounded-md px-3 py-2" href="/dashboard/settings/">Settings</a></li>
        </ul>
    </nav>
    <section class="col-span-9@md col-span-10@lg p-4 p-6@md" aria-labelledby="dashboard-title">
        <div class="flex wrap between x-center gap-3 mb-6">
            <h3 class="text-xl font-semibold m-0" id="dashboard-title">Overview</h3>
            <button class="rounded-md text-sm" type="button">Export report</button>
        </div>
        <ul class="grid cols-1 cols-3@sm gap-4 m-0 p-0" role="list">
            <li class="bg-surface border rounded-lg p-4">
                <p class="text-sm text-muted m-0">Revenue</p>
                <p class="text-2xl font-semibold m-0 mt-1">$48,210</p>
            </li>
            <li class="bg-surface border rounded-lg p-4">
                <p class="text-sm text-muted m-0">Orders</p>
                <p class="text-2xl font-semibold m-0 mt-1">1,284</p>
            </li>
            <li class="bg-surface border rounded-lg p-4">
                <p class="text-sm text-muted m-0">Refund rate</p>
                <p class="text-2xl font-semibold m-0 mt-1">1.8%</p>
            </li>
        </ul>
        <section class="bg-surface border rounded-lg mt-6" aria-labelledby="orders-title">
            <h4 class="text-base font-semibold m-0 px-4 py-3 border-b" id="orders-title">Recent orders</h4>
            <ul class="m-0 p-0 text-sm" role="list">
                <li class="flex between gap-4 px-4 py-3 border-b"><span>Lina Haddad</span><span class="text-muted">$320.00</span></li>
                <li class="flex between gap-4 px-4 py-3 border-b"><span>Omar Siddiqui</span><span class="text-muted">$86.50</span></li>
                <li class="flex between gap-4 px-4 py-3"><span>Grace Okafor</span><span class="text-muted">$1,140.00</span></li>
            </ul>
        </section>
    </section>
</div>

With apply()

The same result with BEM class names and no utility classes. @use "@wovenui/core" as *; emits the tokens and base styles, and each apply() writes one rule.

SCSS
@use "@wovenui/core" as *;

.dashboard {
    @include apply(
        grid, // display: grid
        cols(12) md, // 12 grid columns from md (768px) up
        bg(base), // page background, follows the theme
        text-color(regular), // regular text color, follows the theme
        border(1), // 1px border (width key 1), regular color
        rounded(lg), // corner radius: size key lg (0.75rem)
        overflow-hidden // clip content that overflows the box
    );
}

.dashboard__nav {
    @include apply(
        col-span(3) md, // span 3 of 12 columns from md (768px) up
        col-span(2) lg, // span 2 of 12 columns from lg (1024px) up
        p(4), // padding: spacing key 4 (1rem)
        bg(alternate) // alternate background, follows the theme
    );
}

.dashboard__brand {
    @include apply(
        m(0), // margin: 0
        mb(3), // bottom margin: spacing key 3 (0.75rem)
        font(semibold) // font weight: semibold (600)
    );
}

// A scrolling row below md, a column from md
.dashboard__menu {
    @include apply(
        flex, // display: flex
        col md, // flex direction: column from md (768px) up
        gap(1), // gap: spacing key 1 (0.25rem)
        m(0), // margin: 0
        p(0), // padding: 0
        overflow-x-auto, // scroll sideways when the row is too wide
        font-size(sm) // font size key sm (0.875rem)
    );
}

.dashboard__link {
    @include apply(
        block, // display: block
        px(3), // horizontal padding: spacing key 3 (0.75rem)
        py(2), // vertical padding: spacing key 2 (0.5rem)
        rounded(md), // corner radius: size key md (0.5rem)
        whitespace-nowrap, // keep the text on one line
        text-color(muted), // muted text color, follows the theme
        bg(hover) hover, // hover background on hover
        text-color(regular) hover // regular text color on hover
    );

    &[aria-current="page"] {
        @include apply(
            bg(selected), // selected item background
            text-color(brand), // brand text color
            font(medium) // font weight: medium (500)
        );
    }

    &:focus-visible {
        outline: token(border-focus-width) solid token(border-focus);
        outline-offset: token(border-focus-offset);
    }
}

.dashboard__main {
    @include apply(
        col-span(9) md, // span 9 of 12 columns from md (768px) up
        col-span(10) lg, // span 10 of 12 columns from lg (1024px) up
        p(4), // padding: spacing key 4 (1rem)
        p(6) md // padding key 6 (1.5rem) from md (768px) up
    );
}

.dashboard__header {
    @include apply(
        flex, // display: flex
        wrap, // let items wrap onto new lines
        between, // main axis: space between (justify-content)
        x-center, // cross axis: center (align-items)
        gap(3), // gap: spacing key 3 (0.75rem)
        mb(6) // bottom margin: spacing key 6 (1.5rem)
    );
}

.dashboard__title {
    @include apply(
        m(0), // margin: 0
        font-size(xl), // font size key xl (1.5rem)
        font(semibold) // font weight: semibold (600)
    );
}

.dashboard__button {
    @include apply(
        rounded(md), // corner radius: size key md (0.5rem)
        font-size(sm) // font size key sm (0.875rem)
    );
}

.dashboard__stats {
    @include apply(
        grid, // display: grid
        cols(1), // one grid column
        cols(3) sm, // three columns from sm (640px) up
        gap(4), // gap: spacing key 4 (1rem)
        m(0), // margin: 0
        p(0) // padding: 0
    );
}

.dashboard__stat {
    @include apply(
        p(4), // padding: spacing key 4 (1rem)
        bg(surface), // surface background, follows the theme
        border(1), // 1px border (width key 1), regular color
        rounded(lg) // corner radius: size key lg (0.75rem)
    );
}

.dashboard__label {
    @include apply(
        m(0), // margin: 0
        font-size(sm), // font size key sm (0.875rem)
        text-color(muted) // muted text color, follows the theme
    );
}

.dashboard__value {
    @include apply(
        m(0), // margin: 0
        mt(1), // top margin: spacing key 1 (0.25rem)
        font-size(2xl), // font size key 2xl (1.75rem)
        font(semibold) // font weight: semibold (600)
    );
}

.dashboard__panel {
    @include apply(
        mt(6), // top margin: spacing key 6 (1.5rem)
        bg(surface), // surface background, follows the theme
        border(1), // 1px border (width key 1), regular color
        rounded(lg) // corner radius: size key lg (0.75rem)
    );
}

.dashboard__panel-title {
    @include apply(
        m(0), // margin: 0
        px(4), // horizontal padding: spacing key 4 (1rem)
        py(3), // vertical padding: spacing key 3 (0.75rem)
        font-size(base), // font size key base (1rem)
        font(semibold), // font weight: semibold (600)
        border-bottom(1) // 1px bottom border (width key 1), regular color
    );
}

.dashboard__orders {
    @include apply(
        m(0), // margin: 0
        p(0), // padding: 0
        font-size(sm) // font size key sm (0.875rem)
    );
}

.dashboard__order {
    @include apply(
        flex, // display: flex
        between, // main axis: space between (justify-content)
        gap(4), // gap: spacing key 4 (1rem)
        px(4), // horizontal padding: spacing key 4 (1rem)
        py(3), // vertical padding: spacing key 3 (0.75rem)
        border-bottom(1), // 1px bottom border (width key 1), regular color
        border-bottom(0) last-child // no bottom border when it is the last child
    );
}

.dashboard__amount {
    @include apply(text-color(muted)); // muted text color, follows the theme
}
HTML
<div class="dashboard">
    <nav class="dashboard__nav" aria-label="Dashboard">
        <p class="dashboard__brand">Northwind</p>
        <ul class="dashboard__menu" role="list">
            <li><a class="dashboard__link" href="/dashboard/" aria-current="page">Overview</a></li>
            <li><a class="dashboard__link" href="/dashboard/orders/">Orders</a></li>
            <li><a class="dashboard__link" href="/dashboard/customers/">Customers</a></li>
            <li><a class="dashboard__link" href="/dashboard/settings/">Settings</a></li>
        </ul>
    </nav>
    <section class="dashboard__main" aria-labelledby="dashboard-title">
        <div class="dashboard__header">
            <h3 class="dashboard__title" id="dashboard-title">Overview</h3>
            <button class="dashboard__button" type="button">Export report</button>
        </div>
        <ul class="dashboard__stats" role="list">
            <li class="dashboard__stat">
                <p class="dashboard__label">Revenue</p>
                <p class="dashboard__value">$48,210</p>
            </li>
            <li class="dashboard__stat">
                <p class="dashboard__label">Orders</p>
                <p class="dashboard__value">1,284</p>
            </li>
            <li class="dashboard__stat">
                <p class="dashboard__label">Refund rate</p>
                <p class="dashboard__value">1.8%</p>
            </li>
        </ul>
        <section class="dashboard__panel" aria-labelledby="orders-title">
            <h4 class="dashboard__panel-title" id="orders-title">Recent orders</h4>
            <ul class="dashboard__orders" role="list">
                <li class="dashboard__order"><span>Lina Haddad</span><span class="dashboard__amount">$320.00</span></li>
                <li class="dashboard__order"><span>Omar Siddiqui</span><span class="dashboard__amount">$86.50</span></li>
                <li class="dashboard__order"><span>Grace Okafor</span><span class="dashboard__amount">$1,140.00</span></li>
            </ul>
        </section>
    </section>
</div>

Tokens used

Every color comes from a theme token, so the template follows the light, dark and inverse themes with no extra CSS.

bg-base, bg-surface, bg-alternate
Three surface levels: the page, the tiles and panel, and the side navigation.
text-regular, text-muted
Values and labels.
border-regular
Tile, panel and row rules.
bg-selected, text-brand
The current page in the navigation, matched on aria-current="page" in the SCSS version.
bg-hover
Navigation link hover.
brand, text-on-brand, border-focus
The export button and the focus ring, from the base layer.