Freeframe

Documentation

Dashboard

v1.0.1Updated Oct 18, 2026A polished admin dashboard with live charts, data tables and settings.

Overview#

Dashboard is a polished admin dashboard UI, built as a WordPress block theme. The fictional product is Northstar Admin, the back office of Trailhead Co., an outdoor-gear store. The theme has an overview with KPI cards and an interactive revenue chart, an analytics page (line, stacked bar, funnel and heatmap charts), an orders table with search, status tabs, sorting, pagination and bulk actions, customers with a detail panel, a product catalogue with grid and table views and inventory levels, tabbed settings with dialogs and switches, a sign-in screen and a 404 page, with a collapsible sidebar and light and dark themes. It is version 1.0.0 (see template.json and style.css), works with WordPress 7.1 and PHP 7.4 or newer, and is the WordPress version of the HTML template of the same name. See it live at freeframe.dev/templates/dashboard.

Dashboard is a front end: its numbers, orders, customers and products are a demo data set in assets/js/main.js, and the charts and tables are drawn by that script, as in the HTML version. Use it to prototype or present an admin UI, or as the front of an app whose data comes from your own API. Page headings and descriptions, cards and their labels, KPIs, legends, the activity feed, plans and the settings copy are core WordPress blocks, so you edit them in the block editor. Colours and fonts are design tokens in theme.json: change them in Appearance > Editor > Styles and the whole site follows.

Quick start#

Requirements: WordPress 7.1 or newer and PHP 7.4 or newer. No plugins are needed.

  1. Download dashboard.zip from freeframe.dev/templates/dashboard (the WordPress tab).
  2. In your WordPress admin go to Appearance > Themes > Add New Theme > Upload Theme, choose the zip and click Install Now, then Activate. The theme is listed as Freeframe Dashboard.
  3. Go to Appearance > Dashboard setup and click Create the demo pages. This creates the Overview, Analytics, Orders, Customers, Products, Settings and Sign in pages from the theme's page patterns (Sign in with the Full screen (sign-in) template) and sets Overview as the front page (Settings > Reading). Pages that already exist with the same slug are left alone, so it is safe to click it again.
  4. Visit your site. Edit any page in Pages, and the sidebar, top bar, footer and templates in Appearance > Editor.

You can also try the theme without installing anything: the Try it in WordPress button on freeframe.dev opens it in WordPress Playground (WordPress running in your browser) with the demo pages already set up.

To work on the theme's files locally you need Node.js 24 and pnpm; WordPress itself runs in WordPress Playground, so there is no PHP, web server or database to install:

sh
pnpm install
pnpm dev     # builds the CSS, then starts WordPress with the theme at http://127.0.0.1:9400

pnpm dev signs you in as admin (password password) and creates the demo pages. The site is rebuilt from scratch every time you start it; your edits to theme files are live, edits made in the WordPress admin are not kept.

Pages#

Each page is a page pattern (in Patterns > Dashboard pages) made of section patterns. Every section is also offered on its own in the block inserter under Patterns > Dashboard.

Page Slug Page pattern Sections (pattern files in patterns/)
Overview / page-home.php overview-header, kpis (KPI cards with sparklines), revenue-traffic (revenue chart, traffic donut), recent-activity (recent orders, activity feed, top products)
Analytics /analytics/ page-analytics.php analytics-header, traffic-summary, visitors-chart, revenue-funnel (stacked bars, checkout funnel), order-heatmap, top-pages (top pages, visitors by country)
Orders /orders/ page-orders.php orders-header, orders-tabs (status tabs), orders-table
Customers /customers/ page-customers.php customers-header, customer-summary, customers-table, customer-panel (the detail panel)
Products /products/ page-products.php products-header, inventory-summary, products-tools, products-grid, products-table, products-empty
Settings /settings/ page-settings.php settings-header, settings-tabs, settings-profile, settings-team, settings-billing, settings-notifications, invite-modal, delete-modal
Sign in /login/ page-login.php sign-in-top (logo and theme switch), sign-in (form), sign-in-footer; the right-hand panel is the sign-in-aside pattern in the Full screen template
404 any missing URL templates/404.html 404

The script finds the pages by their slugs: if you rename one, change its URL in freeframe_dashboard_page_urls() in inc/setup.php too (search results, order links and the sign-in redirect use it).

Templates#

Template Used by Layout
page.html pages the app: sidebar, top bar, the page content in <main>, footer
page-full-screen.html (Full screen (sign-in)) Sign in two columns: the page content and the sign-in-aside pattern; no sidebar or top bar
page-with-title.html (Page with title) text pages you add the app layout with the page title and text in a readable column
single.html, index.html, archive.html, search.html posts, blog, archives, search the app layout with a readable column
404.html missing pages the minimal header and footer around the 404 pattern

Project structure#

freeframe-dashboard/
  style.css                 theme header (name, version, requirements, license); no styles
  theme.json                design tokens: palette, fonts, font sizes, layout widths, button; templates and parts
  functions.php             loads the files in inc/
  inc/setup.php             stylesheet and script (with its strings and page URLs), editor styles,
                            dark mode and sidebar script, pattern categories
  inc/icons.php             registers assets/icons/*.svg for the Icon block
  inc/icon-labels.php       the icons' names shown in the editor
  inc/block-styles.php      button block styles
  inc/site-setup.php        Appearance > Dashboard setup: creates the demo pages
  templates/                see Templates above
  parts/                    sidebar.html, header.html, footer.html, header-minimal.html,
                            footer-minimal.html (each shows the pattern of the same name)
  patterns/                 every section and page (PHP files with block markup), the sidebar,
                            top bar, footers, the toast and the sign-in panel
  assets/css/theme.css      the compiled stylesheet (do not edit: built from src/)
  assets/css/editor.css     the same for the block editor (generated by src/editor-css.mjs)
  assets/js/main.js         the app: demo data, charts, tables, menus, dialogs, forms
  assets/icons/             SVG icons for the Icon block
  assets/img/               logo, favicon, social image
  assets/fonts/             Inter and JetBrains Mono, with their licenses
  src/theme.css             the Tailwind CSS source of assets/css/theme.css
  src/sidebar-icons.css     the sidebar menu's icons (CSS masks)
  src/icons.css             icons drawn before or after text with CSS (generated)
  src/editor-css.mjs        writes assets/css/editor.css after each build
  languages/                freeframe-dashboard.pot, the template for translations
  blueprint.json            WordPress Playground setup used by `pnpm dev`
  screenshot.png            the picture shown in Appearance > Themes
  package.json              the CSS build and the local WordPress (Node.js tooling)
  readme.txt, README.md, DOCS.md, CHANGELOG.md, LICENSE.txt

Customising#

Brand and colours#

The colours are in theme.json (settings.color.palette) and in Appearance > Editor > Styles > Colors > Palette:

Colours Used for
Brand 50 to Brand 950 the indigo of primary buttons, links, the current menu item, badges and focus rings
Zinc 50 to Zinc 950, White, Black neutrals, for your own blocks
Base and Contrast the page background (#fafafa) and text; they switch to dark values in dark mode

The theme's stylesheet reads the Brand colours from the palette, so changing Brand 600 in Styles recolours every primary button and accent at once. Pick a full ramp of shades (light 50 to dark 950) so hover states and dark mode keep their contrast. The chart colours are CSS variables (--chart-1 to --chart-5, --chart-grid, --chart-text, --heat-0) at the top of src/theme.css, with a second set for dark mode; the script reads them.

Typography#

Two fonts are bundled in assets/fonts/ and declared in theme.json (settings.typography.fontFamilies), so no request goes to Google: Inter (variable, 400 to 700; preset sans) for everything and JetBrains Mono (400 and 500; preset mono) for order numbers, SKUs and codes. To use other fonts, install them in Styles > Typography > Fonts (the Font Library) and select them, or replace the fontFace entries in theme.json. Font sizes are the presets in theme.json (xs 0.75rem to 9xl 8rem, the Tailwind scale).

Content#

Edit pages in Pages: click any text to change it. Sections are groups; open List View (the stacked lines icon at the top left) to select a section, move it, duplicate it or delete it. To add a section from another page, use the block inserter (+) > Patterns > Dashboard.

What the script draws or drives is in Custom HTML blocks: edit those in the block's HTML view, and keep their ids and data- attributes.

Pattern Custom HTML What it is
*-header the action buttons buttons that show a demo message (data-toast-msg) or link to a page
kpis, revenue-traffic, visitors-chart, revenue-funnel, order-heatmap sparklines, charts, their range buttons and legends drawn from the demo data, see Charts
recent-activity, top-pages, orders-table, customers-table, products-* tables, lists, search, filters, pagination filled from the demo data
orders-tabs, settings-tabs tabs see Settings
settings-* forms, plan and card buttons, the team list, notification switches see Forms
customer-panel, invite-modal, delete-modal the panel and dialogs opened by the script
sign-in, sign-in-top, sign-in-aside the sign-in form and buttons, the theme switch, the panel's chart
header, sidebar search, the theme switch, notifications and account menus; the sidebar's buttons

A few blocks carry a class (Block settings > Advanced > Additional CSS class(es)) that the script or stylesheet reads:

  • ff-tabpanel on each settings section (the script gives it its tab panel role and label), and ff-hidden on the sections hidden until their tab is chosen. In the editor hidden sections are shown with a dashed outline.
  • ffd-stat-products, ffd-stat-low, ffd-stat-out, ffd-stat-value on the inventory numbers and ffd-current-plan on the plan name: the script writes into them (blocks can't store data- attributes, so the script turns these classes back into them). ffd-plan-card marks each plan card.
  • reveal fades a card in as it scrolls into view; card, card-head and card-title are the card styles; badge badge-green (-amber, -red, -zinc, -blue) are the coloured badges.

The demo data

All demo data is generated in section 2 ("Demo data") of assets/js/main.js: TODAY (the fixed date the demo is built around), NAMES, CITIES, CHANNELS, STATUSES, PRODUCTS (name, SKU, category, price, stock, capacity, units sold, colour and icon), ORDERS (48 generated orders) and CUSTOMERS (derived from the names and orders). Chart series are in section 8. To use real data, replace these lists with data from your API (for example fetch('/api/orders').then(r => r.json())) and call the same render code once it arrives; set TODAY to new Date(). Make these changes in a child theme (copy assets/js/main.js there and load it instead), or they are overwritten when you update the theme. The static numbers in the KPI cards, the analytics summary, the activity feed and the settings are blocks: edit them in the editor.

Images and artwork#

There are no photos. Avatars are generated initials, product images are SVG icons on a coloured tile (thumb() in section 11 of the script), and charts are SVG. The logo is assets/img/logo.svg with the name "Northstar" in a paragraph link at the top of the sidebar (and in the minimal header and the sign-in page); edit them in Appearance > Editor > Patterns. The site name in browser tabs comes from Settings > General > Site Title. The social image is assets/img/og-image.png (1200 × 630, source og-image.svg); the favicon assets/img/favicon.svg is used until you set a Site Icon in Settings > General.

The app layout's parts are in Appearance > Editor > Patterns: Sidebar, Header (the top bar), Footer, and the minimal header and footer of the 404 page.

The sidebar menu is two List blocks of links (Workspace and Account). Each item's icon is a class on the list item: icon-before-overview, icon-before-analytics, icon-before-orders, icon-before-customers, icon-before-products, icon-before-settings, icon-before-help, with icon-size-20 (the artwork is in src/sidebar-icons.css; add your own there and run pnpm build). Each link has the class nav-item and its text a sb-label span (hidden when the sidebar is collapsed). The script marks the link to the current page with aria-current="page". To add a page, add a list item, and an entry to the SEARCH list (section 5 of the script) so global search finds it.

The top bar is Custom HTML: the search box, the dark mode switch, the notifications menu and the account menu.

Dark mode#

A small script printed first in <head> (freeframe_dashboard_theme_script() in inc/setup.php) applies the saved theme and sidebar state before the page paints. The switches ([data-theme-toggle]) toggle the dark class on <html> and save ns-theme in localStorage; without a saved choice the visitor's system setting decides. To always show the light theme, remove the switches from the Header, Header (minimal) and sign-in patterns and add this to a child theme's functions.php:

add_filter( 'freeframe_dashboard_dark_mode', '__return_false' );

Features#

On wide screens the sidebar can be collapsed to icons; the choice is saved as ns-sidebar. Collapsed links keep accessible names (aria-label) and tooltips, and the collapse button is then named "Expand sidebar" (with aria-expanded="false"). Below 1024 px the sidebar becomes a drawer with a backdrop: focus is trapped inside, Escape or the backdrop closes it, and focus returns to the menu button.

Ctrl K / ⌘ K or / focuses the search box in the top bar. Results come from SEARCH (pages, settings sections, every customer, order and product) and are navigable with the arrow keys and Enter. Results link to filtered pages, such as /orders/?q=NS-4821 or /customers/?q=Olivia%20Martin.

Dropdown menus (notifications, account) open on click and close with Escape or a click outside; arrow keys move between items. Elements with data-modal are dialogs: data-modal-open="<id>" opens one, data-modal-close closes it, focus is trapped and returned. Any element with data-toast-msg="..." shows a short status message when clicked.

Charts#

Section 7 of the script contains the chart functions: sparkline(el), lineChart(el, cfg), donut(el, segments, centerLabel), barChart(el, cfg), funnel(el, steps) and heatmap(el). They redraw on resize and show tooltips on hover. Line charts are also focusable: the Left/Right arrow keys move from point to point and show each value, and Escape hides it. The overview's range buttons switch the revenue chart between 7 days, 30 days and 12 months; clicking a legend item on the analytics page hides that line.

Orders table#

The orders page filters by status tab and search (also from ?q=), sorts by any column header (aria-sort is updated), paginates 10 rows per page, and supports selecting rows with a bulk-action bar ("Mark as paid", "Export", "Clear"). The export is a demo and only shows a message.

Customers and products#

The customers page filters by segment and search and opens a slide-over detail panel with recent orders and a notes form. The products page filters by category and switches between grid and table views; the choice is saved as ns-products-view. Stock shows as a meter with "In stock", "Low stock" or "Out of stock".

Settings#

The tabs support arrow keys, Home and End, and update the URL hash, so /settings/#team and /settings/#billing open those tabs. The invite dialog adds a member to the list, plan buttons switch the current plan, notification switches are role="switch" buttons, and the bio field has a 240-character counter.

Back to top#

A button appears after scrolling about one screen down; it scrolls to the top (instantly with reduced motion), moves keyboard focus to the main content and lifts above the toast. It is created by assets/js/main.js; to remove it, delete the "Back to top" part of that file and the .back-to-top styles in src/theme.css, then run pnpm build.

Block styles#

Block Styles
Button Fill (default, the primary indigo), Secondary

Forms#

Forms with data-validate are checked on submit: required fields, email format and minlength. Errors appear under each field, linked with aria-describedby, and focus moves to the first problem. A valid form then calls the handler named by data-on-valid (from the HANDLERS object in the script) or shows the message in data-success.

Form Page Behaviour
Sign in (data-on-valid="signin") Sign in email and password (8+ characters); HANDLERS.signin goes to the Overview
Profile (data-success="Profile saved") Settings name, email, bio and other profile fields; shows a message
Invite (data-on-valid="invite") Settings dialog email and role; HANDLERS.invite adds the member to the team list
Customer note Customers panel note text; shows a message

Nothing is saved or sent. To connect your API, look for the comment // Demo only: nothing is sent. in bindForms() (section 6 of the script) and send the data before showing the success state, giving each form an action in its Custom HTML. Replace HANDLERS.signin with a call to your authentication provider. A dashboard should only be reachable after a real sign-in: protect it on your server, for example by making the pages private, not in this front-end code.

Saved data (localStorage)#

Key Values Used for
ns-theme "dark" or "light" colour theme
ns-sidebar "collapsed" or "open" desktop sidebar state
ns-products-view "grid" or "table" products page view

These are the HTML version's keys. Each value is compared with the exact expected strings when read, so a missing or tampered value falls back to the default (system theme, open sidebar, grid view). Visitors can reset them by clearing site data.

SEO and sharing#

Page titles come from WordPress: the page title, a middle dot and the site name (for example "Orders · Northstar Admin"); the front page shows the site name and tagline (Settings > General). The theme doesn't output meta descriptions or Open Graph tags; an admin UI usually shouldn't be indexed at all: tick Settings > Reading > Discourage search engines or make the pages private.

Accessibility#

Built in: a skip link to the main content (added by WordPress, styled by the theme), landmarks (sidebar, top bar, labelled navigation, main, footer), one h1 per page, visible focus rings, a labelled dark mode switch with aria-pressed, a drawer and dialogs that trap focus, menus with arrow keys, a search combobox with a listbox, accessible tabs and switches, charts readable with the keyboard, sortable table headers with aria-sort, live status messages, form errors announced through aria-describedby and aria-invalid, and reduced-motion support. The demo passes axe (WCAG 2.1 A and AA) in light and dark mode.

When you edit: keep one Heading 1 per page, keep link text meaningful, and label any field you add to a form (<label for="...">).

Building and deploying#

You only need this if you change the theme's files. The stylesheet assets/css/theme.css is compiled with Tailwind CSS from src/theme.css and the classes used in patterns/, parts/, templates/, inc/ and assets/js/:

sh
pnpm install
pnpm build   # compile assets/css/theme.css and assets/css/editor.css
pnpm watch   # recompile assets/css/theme.css on every change (run pnpm build before you commit)

Run pnpm build after adding a utility class to a pattern, template or the script's markup, otherwise the class has no style. Classes you type in the editor's Additional CSS class(es) field are only styled if the stylesheet already contains them.

To deploy, zip the theme folder (named freeframe-dashboard, without node_modules/) and upload it in Appearance > Themes, or copy the folder to wp-content/themes/ on your server.

Translations#

The theme is translation-ready with the text domain freeframe-dashboard. All text in patterns, templates and PHP goes through WordPress's translation functions; the script's interface strings (messages, counts, search entries, form errors) are translated in PHP (freeframe_dashboard_script_strings() in inc/setup.php). The demo data (names, products, statuses, chart labels) is data in assets/js/main.js. Create a translation with Loco Translate, or with WP-CLI and Poedit:

sh
wp i18n make-pot wp-content/themes/freeframe-dashboard wp-content/themes/freeframe-dashboard/languages/freeframe-dashboard.pot

Save freeframe-dashboard-de_DE.mo (for example) in wp-content/languages/themes/ or in the theme's languages/ folder. Page content created by Dashboard setup is copied into your pages in the site's language at that moment; translate it in the editor like any other content.

Child themes#

Use a child theme for changes you want to keep when Dashboard is updated. Create wp-content/themes/dashboard-child/style.css:

css
/*
Theme Name: Dashboard Child
Template: freeframe-dashboard
*/

Templates, parts, patterns and theme.json in the child theme override the parent's files of the same name. To use your own copy of assets/js/main.js, dequeue the parent's script (freeframe-dashboard) and enqueue yours in the child's functions.php. The parent's stylesheet only contains the classes the parent uses: if your child theme uses new utility classes, compile a stylesheet for the child (copy package.json and src/theme.css, point the @source lines at the child's folders) and enqueue it.

Troubleshooting#

  • The pages look unstyled or new classes do nothing. The stylesheet only contains classes it found when it was built. Run pnpm build after editing patterns, then reload.
  • "This block contains unexpected or invalid content". A pattern or page was edited by hand with markup that doesn't match the block. Click Attempt recovery, or re-insert the section from Patterns > Dashboard.
  • A chart or table is empty. Its Custom HTML lost the data- attribute or id the script looks for; re-insert the section from Patterns > Dashboard.
  • The Sign in page shows the sidebar. Choose the Full screen (sign-in) template in the page's settings.
  • Search results or the sign-in redirect go to a missing page. Update freeframe_dashboard_page_urls() in inc/setup.php after renaming pages.
  • Dashboard setup says the pages exist, but they are empty. It never overwrites existing pages. Delete (and empty the trash of) the pages with those slugs, or open each page and insert its page pattern from Patterns > Dashboard pages.
  • The theme can't be installed on an older WordPress. Dashboard uses the Icon block's icon collections from WordPress 7.1; update WordPress first.

License#

MIT, which is compatible with the GPL that WordPress requires (see LICENSE.txt in the theme folder). Free for personal and commercial use. Inter and JetBrains Mono are licensed under the SIL Open Font License 1.1 (OFL.txt in each folder of assets/fonts/).

Search documentation

Search the guides and every template's documentation