# Vitral — the whole documentation > Generated from https://vitralui.github.io/vitral/ on 2026-09-25. # Vitral > A Vue 3 component library built around a token engine: design tokens, presets, light and dark, pass-through and an unstyled mode. Source: https://vitralui.github.io/vitral/ Vitral is a Vue component library with a theme engine underneath: change a few tokens and every control follows. ## Everything an interface needs, in one set Built to be re-dressed rather than restyled, and tested the way people actually use it: with a keyboard and a screen reader. - **Design tokens**: Primitive, semantic and component layers, compiled to CSS variables. Change the primary colour and everything follows. - **Built to the ARIA patterns**: Each component follows its WAI-ARIA pattern and ships with a spec that runs axe and drives the keyboard. Text contrast is measured across every preset and both schemes, so a theme cannot quietly become unreadable. - **Yours to override**: Reach any element with pass-through, retoken one instance, or go unstyled and bring your own CSS. - **A predictable API**: The same props, slots and events everywhere: size, variant, invalid, fluid. Learning one component covers most of the next. - **Light and dark**: Both schemes come out of the same preset, and follow the operating system unless the reader says otherwise. - **Icons included**: An outline set drawn for the library, tree-shaken per icon. Any other icon library works too. - **Application pieces**: Charts, a scheduler and a task board sit next to the form controls, themed by the same tokens. - **Forms and validation**: Field state, rules and async checks with no dependency, plus resolvers for Zod, Yup, Valibot and Superstruct. - **Server rendering**: Styles are collected during the render and go into the head, so nothing arrives unstyled. Nuxt takes one line in nuxt.config. ## 105 components, grouped the way you look for them From a text box to a scheduler, each with live examples and its API. - [**Form**30AutoComplete · CascadeSelect · Checkbox · ColorPicker](http://localhost:4185/vitral/components/autocomplete/) - [**Button**4Button · ButtonGroup · SpeedDial · SplitButton](http://localhost:4185/vitral/components/button/) - [**Data**16Chart · DataGrid · DataTable · DataView](http://localhost:4185/vitral/components/chart/) - [**Panel**9Accordion · Card · Divider · Fieldset](http://localhost:4185/vitral/components/accordion/) - [**Overlay**8ConfirmDialog · ConfirmPopup · Dialog · Drawer](http://localhost:4185/vitral/components/confirmdialog/) - [**Menu**10Breadcrumb · Command · ContextMenu · Dock](http://localhost:4185/vitral/components/breadcrumb/) - [**Messages**8BlockUI · Chat · Message · MeterGroup](http://localhost:4185/vitral/components/blockui/) - [**Media**7AspectRatio · Avatar · Carousel · Cropper](http://localhost:4185/vitral/components/aspectratio/) - [**Layout**7DockPanel · Grid · Splitter · SplitView](http://localhost:4185/vitral/components/dockpanel/) - [**Misc**6Badge · Chip · Inplace · ScrollTop](http://localhost:4185/vitral/components/badge/) ## Your brand in a few lines A preset is a tree of tokens over another preset. Pick a colour below and the whole site, this page included, changes with it. ```ts theme.ts import { definePreset, palette, Prism } from '@vitral/vue'; export const Brand = definePreset(Prism, { semantic: { primary: palette('#2563eb') }, components: { button: { root: { borderRadius: '999px' } } } }); ``` ### Notifications Choose what reaches your inbox. ## Templates to start from Whole applications made of these components: responsive, themed, and a click away from a live demo. - [**Fernhill Supply**Commerce · 4 screens](http://localhost:4185/vitral/templates/ecommerce/) - [**Lumen Metrics**Dashboard · 3 screens](http://localhost:4185/vitral/templates/saas/) - [**The Lantern**Publishing · 3 screens](http://localhost:4185/vitral/templates/news/) - [**Field Notes**Publishing · 3 screens](http://localhost:4185/vitral/templates/blog/) - [**Northgate Homes**Commerce · 3 screens](http://localhost:4185/vitral/templates/realestate/) - [**Skylark Air**Travel · 4 screens](http://localhost:4185/vitral/templates/airline/) ## Start with one package One plugin call, or one line in `nuxt.config`, and a theme that answers to `useTheme()`. --- # Icons > The outline icon set drawn for Vitral, searchable by name, category and tag. Copy any icon as an import or as SVG. Source: https://vitralui.github.io/vitral/icons/ One outline set on a 24×24 grid: round 2-unit strokes, no fill, in the current text colour. Each icon is a named export of `@vitral/icons`, so an application bundles only the ones it imports. Size and stroke come from the theme's `icon.size` and `icon.strokeWidth` tokens; `size` and `stroke-width` override them on one icon. 799 icons ## Arrows & navigation 63 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ## Interface & actions 93 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ## Shapes 20 - - - - - - - - - - - - - - - - - - - - ## Files & folders 32 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ## Text & formatting 37 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ## Media 44 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ## Communication 33 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ## People & accounts 19 - - - - - - - - - - - - - - - - - - - ## Devices & hardware 28 - - - - - - - - - - - - - - - - - - - - - - - - - - - - ## Development 31 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ## Charts & data 20 - - - - - - - - - - - - - - - - - - - - ## Maps & location 19 - - - - - - - - - - - - - - - - - - - ## Transport 21 - - - - - - - - - - - - - - - - - - - - - ## Time & calendar 19 - - - - - - - - - - - - - - - - - - - ## Weather & nature 37 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ## Security 20 - - - - - - - - - - - - - - - - - - - - ## Health & medical 22 - - - - - - - - - - - - - - - - - - - - - - ## Home & buildings 26 - - - - - - - - - - - - - - - - - - - - - - - - - - ## Food & drink 30 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ## School & education 36 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ## E-commerce 32 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ## Finance 32 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ## Games 50 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ## Sports 35 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ## Icons from other libraries Every `icon` prop, on a button or a menu item or a toast or a tree node, goes through ``, which takes more than this set: another library's Vue component, SVG markup you trust, or the classes of an icon font. Whatever it is gets the `vt-icon` class, the theme's size and the same hidden-or-labelled accessibility. ``` ``` --- # Charts > Every kind of chart the engine draws, live on one page: line, area, bar, lollipop, scatter, bubble, heat map, candlestick, pie, donut and radar. Source: https://vitralui.github.io/vitral/charts/ One SVG engine, no plotting library underneath, driven by the options object ApexCharts made familiar. Everything visual is plain data, so it survives `JSON` and a settings screen can write it. Each chart is named by a summary generated from its own data, and its plot takes focus: the arrow keys read it point by point. [The Chart component, in full](http://localhost:4185/vitral/components/chart/) ## Over time ### Line One value as it moves. The chart to reach for first, and the one a reader never has to be taught. ```vue template ``` ```ts script const series = [ { name: 'Visitors', data: [ 310, 402, 385, 520, 610, 580, 720, 690, 810, 760, 905, 980 ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { show: false }, xaxis: { categories: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] }, stroke: { curve: 'smooth', width: 3 } }; ``` ### Line, several series - - - Two or three lines compare shapes. Past four, the plot becomes a knot and a small multiple reads better. ```vue template ``` ```ts script const series = [ { name: 'API', data: [ 120, 150, 140, 180, 210, 190, 240, 260, 250, 290, 310, 330 ] }, { name: 'Web', data: [ 80, 90, 110, 100, 130, 150, 140, 160, 180, 170, 200, 220 ] }, { name: 'Jobs', data: [ 30, 28, 35, 40, 38, 42, 46, 50, 48, 53, 56, 60 ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { position: 'bottom' }, xaxis: { categories: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] }, stroke: { curve: 'smooth', width: 2 }, markers: { size: 0 } }; ``` ### Area, stacked - - - A total over time, and what it is made of. Stacked, the top edge is the total; unstacked, the fills overlap and mislead. ```vue template ``` ```ts script const series = [ { name: 'API', data: [ 120, 150, 140, 180, 210, 190, 240, 260, 250, 290, 310, 330 ] }, { name: 'Web', data: [ 80, 90, 110, 100, 130, 150, 140, 160, 180, 170, 200, 220 ] }, { name: 'Jobs', data: [ 30, 28, 35, 40, 38, 42, 46, 50, 48, 53, 56, 60 ] } ]; const options = { chart: { stacked: true, toolbar: { show: false } }, legend: { position: 'bottom' }, xaxis: { categories: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] }, stroke: { curve: 'monotoneCubic', width: 2 } }; ``` ### Range area The band between a low and a high, month by month: a forecast’s spread, or the day’s range. One fill, two edges. ```vue template ``` ```ts script const series = [ { name: 'Forecast', data: [ { x: 'Jan', y: [ 40, 70 ] }, { x: 'Feb', y: [ 44, 76 ] }, { x: 'Mar', y: [ 48, 82 ] }, { x: 'Apr', y: [ 52, 88 ] }, { x: 'May', y: [ 56, 94 ] }, { x: 'Jun', y: [ 60, 100 ] }, { x: 'Jul', y: [ 64, 106 ] }, { x: 'Aug', y: [ 68, 112 ] }, { x: 'Sep', y: [ 72, 118 ] }, { x: 'Oct', y: [ 76, 124 ] }, { x: 'Nov', y: [ 80, 130 ] }, { x: 'Dec', y: [ 84, 136 ] } ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { show: false }, stroke: { curve: 'smooth', width: 2 } }; ``` ### Stream - - - - - A stacked area floating on a baseline of its own. The bands share the movement instead of the bottom one carrying it, which is what makes the shape readable. ```vue template ``` ```ts script const series = [ { name: 'Search', data: [ 30, 43, 53, 57, 55, 49, 41, 36, 36, 42, 53, 66 ] }, { name: 'Direct', data: [ 53, 55, 52, 44, 35, 29, 27, 31, 41, 53, 64, 70 ] }, { name: 'Social', data: [ 55, 49, 41, 36, 36, 42, 53, 66, 79, 87, 89, 85 ] }, { name: 'Mail', data: [ 41, 35, 33, 37, 47, 59, 70, 76, 77, 72, 63, 55 ] }, { name: 'Referral', data: [ 36, 42, 53, 66, 79, 87, 89, 85, 78, 71, 67, 69 ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { position: 'bottom' }, xaxis: { categories: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] } }; ``` ### Bars and a line - - Two marks on one plot: the amount as bars, the rate as a line, each on its own axis. Every series names the mark it wants. ```vue template ``` ```ts script const series = [ { name: 'Revenue', type: 'bar', data: [ 44, 55, 41, 67, 22, 43, 58, 63, 60, 66, 70, 81 ] }, { name: 'Margin', type: 'line', data: [ 12, 14, 13, 18, 9, 12, 15, 17, 16, 18, 19, 22 ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { position: 'bottom' }, xaxis: { categories: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] }, yaxis: [ { title: { text: 'Revenue' } }, { opposite: true, title: { text: 'Margin' } } ], stroke: { width: [ 0, 3 ], curve: 'smooth' } }; ``` ### Candlestick Four numbers a period: open, high, low and close. Each point is a tuple, in that order. ```vue template ``` ```ts script const series = [ { name: 'ACME', data: [ [ 'Mon', 51, 58, 50, 56 ], [ 'Tue', 56, 60, 55, 55 ], [ 'Wed', 55, 57, 51, 52 ], [ 'Thu', 52, 59, 52, 58 ], [ 'Fri', 58, 64, 57, 63 ], [ 'Sat', 63, 65, 60, 61 ], [ 'Sun', 61, 66, 60, 65 ] ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { show: false } }; ``` ### Calendar A year of days as a grid of weeks. A line of 365 points shows the trend; this shows the day, which is what a record of daily activity is read for. ```vue template ``` ```ts script const series = [ { name: 'Commits', data: [ { x: 1767225600000, y: 3 }, { x: 1767312000000, y: 12 }, { x: 1767484800000, y: 1 }, { x: 1767571200000, y: 1 }, { x: 1767657600000, y: 9 }, { x: 1767744000000, y: 3 }, { x: 1767916800000, y: 1 }, { x: 1768089600000, y: 2 }, { x: 1768176000000, y: 2 }, { x: 1768262400000, y: 5 }, { x: 1768348800000, y: 4 }, { x: 1768435200000, y: 1 }, { x: 1768521600000, y: 4 }, { x: 1768608000000, y: 2 }, { x: 1768694400000, y: 2 }, { x: 1768780800000, y: 10 }, { x: 1769040000000, y: 12 }, { x: 1769126400000, y: 10 }, { x: 1769212800000, y: 2 }, { x: 1769299200000, y: 4 }, { x: 1769472000000, y: 6 }, { x: 1769558400000, y: 4 }, { x: 1769644800000, y: 7 }, { x: 1769731200000, y: 12 }, { x: 1769990400000, y: 12 }, { x: 1770076800000, y: 13 }, { x: 1770163200000, y: 10 }, { x: 1770249600000, y: 1 }, { x: 1770336000000, y: 9 }, { x: 1770422400000, y: 1 }, { x: 1770508800000, y: 1 }, { x: 1770595200000, y: 7 }, { x: 1770681600000, y: 13 }, { x: 1770768000000, y: 3 }, { x: 1770940800000, y: 3 }, { x: 1771200000000, y: 10 }, { x: 1771286400000, y: 6 }, { x: 1771372800000, y: 2 }, { x: 1771459200000, y: 14 }, { x: 1771545600000, y: 11 }, { x: 1771632000000, y: 4 }, { x: 1771718400000, y: 1 }, { x: 1771804800000, y: 8 }, { x: 1771891200000, y: 13 }, { x: 1771977600000, y: 3 }, { x: 1772150400000, y: 1 }, { x: 1772323200000, y: 2 }, { x: 1772409600000, y: 2 }, { x: 1772582400000, y: 10 }, { x: 1772668800000, y: 4 }, { x: 1772755200000, y: 2 }, { x: 1772841600000, y: 4 }, { x: 1772928000000, y: 2 }, { x: 1773014400000, y: 8 }, { x: 1773100800000, y: 10 }, { x: 1773187200000, y: 3 }, { x: 1773360000000, y: 3 }, { x: 1773446400000, y: 2 }, { x: 1773532800000, y: 1 }, { x: 1773619200000, y: 14 }, { x: 1773792000000, y: 7 }, { x: 1773878400000, y: 12 }, { x: 1773964800000, y: 9 }, { x: 1774051200000, y: 2 }, { x: 1774137600000, y: 4 }, { x: 1774224000000, y: 8 }, { x: 1774310400000, y: 5 }, { x: 1774396800000, y: 6 }, { x: 1774483200000, y: 13 }, { x: 1774569600000, y: 11 }, { x: 1774742400000, y: 4 }, { x: 1774915200000, y: 5 }, { x: 1775088000000, y: 13 }, { x: 1775174400000, y: 5 }, { x: 1775260800000, y: 3 }, { x: 1775347200000, y: 3 }, { x: 1775692800000, y: 1 }, { x: 1775779200000, y: 11 }, { x: 1775952000000, y: 1 }, { x: 1776038400000, y: 9 }, { x: 1776211200000, y: 9 }, { x: 1776556800000, y: 3 }, { x: 1776643200000, y: 9 }, { x: 1776729600000, y: 3 }, { x: 1776816000000, y: 3 }, { x: 1776902400000, y: 4 }, { x: 1776988800000, y: 5 }, { x: 1777248000000, y: 9 }, { x: 1777334400000, y: 2 }, { x: 1777420800000, y: 1 }, { x: 1777507200000, y: 11 }, { x: 1777593600000, y: 1 }, { x: 1777680000000, y: 1 }, { x: 1777766400000, y: 2 }, { x: 1777852800000, y: 4 }, { x: 1777939200000, y: 1 }, { x: 1778025600000, y: 1 }, { x: 1778198400000, y: 12 }, { x: 1778284800000, y: 4 }, { x: 1778371200000, y: 4 }, { x: 1778544000000, y: 13 }, { x: 1778630400000, y: 2 }, { x: 1778803200000, y: 9 }, { x: 1778889600000, y: 2 }, { x: 1779062400000, y: 7 }, { x: 1779148800000, y: 14 }, { x: 1779235200000, y: 11 }, { x: 1779321600000, y: 5 }, { x: 1779580800000, y: 3 }, { x: 1779667200000, y: 2 }, { x: 1779840000000, y: 9 }, { x: 1779926400000, y: 13 }, { x: 1780012800000, y: 1 }, { x: 1780099200000, y: 2 }, { x: 1780272000000, y: 10 }, { x: 1780358400000, y: 14 }, { x: 1780444800000, y: 2 }, { x: 1780617600000, y: 13 }, { x: 1780876800000, y: 2 }, { x: 1780963200000, y: 10 }, { x: 1781049600000, y: 4 }, { x: 1781136000000, y: 1 }, { x: 1781222400000, y: 1 }, { x: 1781308800000, y: 1 }, { x: 1781395200000, y: 3 }, { x: 1781481600000, y: 12 }, { x: 1781568000000, y: 4 }, { x: 1781654400000, y: 1 }, { x: 1781740800000, y: 11 }, { x: 1781827200000, y: 6 }, { x: 1781913600000, y: 2 }, { x: 1782000000000, y: 2 }, { x: 1782086400000, y: 5 }, { x: 1782172800000, y: 9 }, { x: 1782259200000, y: 1 }, { x: 1782345600000, y: 9 }, { x: 1782432000000, y: 2 }, { x: 1782518400000, y: 4 }, { x: 1782604800000, y: 2 }, { x: 1782777600000, y: 5 }, { x: 1782950400000, y: 2 }, { x: 1783036800000, y: 9 }, { x: 1783209600000, y: 3 }, { x: 1783296000000, y: 3 }, { x: 1783382400000, y: 3 }, { x: 1783555200000, y: 2 }, { x: 1783641600000, y: 12 }, { x: 1783728000000, y: 4 }, { x: 1783900800000, y: 10 }, { x: 1783987200000, y: 6 }, { x: 1784073600000, y: 11 }, { x: 1784160000000, y: 13 }, { x: 1784419200000, y: 3 }, { x: 1784505600000, y: 13 }, { x: 1784678400000, y: 8 }, { x: 1784764800000, y: 4 }, { x: 1784851200000, y: 1 }, { x: 1785196800000, y: 3 }, { x: 1785283200000, y: 4 }, { x: 1785369600000, y: 13 }, { x: 1785456000000, y: 10 }, { x: 1785542400000, y: 3 }, { x: 1785715200000, y: 5 }, { x: 1785801600000, y: 3 }, { x: 1785888000000, y: 1 }, { x: 1785974400000, y: 6 }, { x: 1786060800000, y: 9 }, { x: 1786147200000, y: 2 }, { x: 1787529600000, y: 13 }, { x: 1787616000000, y: 4 }, { x: 1787702400000, y: 3 }, { x: 1787875200000, y: 14 }, { x: 1787961600000, y: 3 }, { x: 1788134400000, y: 11 }, { x: 1788220800000, y: 12 }, { x: 1788307200000, y: 6 }, { x: 1788393600000, y: 5 }, { x: 1788480000000, y: 12 }, { x: 1788652800000, y: 2 }, { x: 1788739200000, y: 2 }, { x: 1788825600000, y: 2 }, { x: 1788912000000, y: 9 }, { x: 1788998400000, y: 2 }, { x: 1789084800000, y: 10 }, { x: 1789257600000, y: 4 }, { x: 1789344000000, y: 7 }, { x: 1789516800000, y: 8 }, { x: 1789603200000, y: 5 }, { x: 1789689600000, y: 9 }, { x: 1789776000000, y: 4 }, { x: 1789862400000, y: 3 }, { x: 1789948800000, y: 12 }, { x: 1790035200000, y: 2 }, { x: 1790121600000, y: 13 }, { x: 1790208000000, y: 2 }, { x: 1790294400000, y: 4 }, { x: 1790467200000, y: 1 }, { x: 1790553600000, y: 8 }, { x: 1790640000000, y: 8 }, { x: 1790726400000, y: 7 }, { x: 1790812800000, y: 14 }, { x: 1790899200000, y: 6 }, { x: 1791072000000, y: 2 }, { x: 1791158400000, y: 14 }, { x: 1791244800000, y: 13 }, { x: 1791331200000, y: 3 }, { x: 1791417600000, y: 6 }, { x: 1791504000000, y: 4 }, { x: 1791590400000, y: 3 }, { x: 1791676800000, y: 2 }, { x: 1791763200000, y: 13 }, { x: 1791849600000, y: 7 }, { x: 1791936000000, y: 11 }, { x: 1792108800000, y: 3 }, { x: 1792281600000, y: 3 }, { x: 1792368000000, y: 1 }, { x: 1792454400000, y: 8 }, { x: 1792540800000, y: 11 }, { x: 1792627200000, y: 11 }, { x: 1792713600000, y: 1 }, { x: 1792800000000, y: 1 }, { x: 1792886400000, y: 1 }, { x: 1792972800000, y: 13 }, { x: 1793059200000, y: 14 }, { x: 1793145600000, y: 4 }, { x: 1793232000000, y: 8 }, { x: 1793318400000, y: 13 }, { x: 1793404800000, y: 2 }, { x: 1793577600000, y: 4 }, { x: 1793750400000, y: 2 }, { x: 1793836800000, y: 4 }, { x: 1793923200000, y: 10 }, { x: 1794009600000, y: 4 }, { x: 1794096000000, y: 1 }, { x: 1794182400000, y: 2 }, { x: 1794268800000, y: 1 }, { x: 1794355200000, y: 2 }, { x: 1794441600000, y: 11 }, { x: 1794528000000, y: 1 }, { x: 1794614400000, y: 1 }, { x: 1794700800000, y: 4 }, { x: 1794873600000, y: 10 }, { x: 1794960000000, y: 8 }, { x: 1795132800000, y: 8 }, { x: 1795219200000, y: 3 }, { x: 1795392000000, y: 10 }, { x: 1795478400000, y: 8 }, { x: 1795564800000, y: 12 }, { x: 1795737600000, y: 11 }, { x: 1795824000000, y: 4 }, { x: 1795910400000, y: 2 }, { x: 1795996800000, y: 1 }, { x: 1796083200000, y: 7 }, { x: 1796169600000, y: 13 }, { x: 1796256000000, y: 3 }, { x: 1796342400000, y: 13 }, { x: 1796601600000, y: 13 }, { x: 1796688000000, y: 4 }, { x: 1796774400000, y: 14 }, { x: 1796860800000, y: 8 }, { x: 1796947200000, y: 10 }, { x: 1797120000000, y: 1 }, { x: 1797206400000, y: 8 }, { x: 1797379200000, y: 3 }, { x: 1797552000000, y: 1 }, { x: 1797638400000, y: 3 }, { x: 1797724800000, y: 2 }, { x: 1797811200000, y: 9 }, { x: 1797897600000, y: 14 }, { x: 1797984000000, y: 11 }, { x: 1798070400000, y: 11 }, { x: 1798156800000, y: 7 }, { x: 1798329600000, y: 3 }, { x: 1798416000000, y: 2 }, { x: 1798502400000, y: 2 }, { x: 1798588800000, y: 13 }, { x: 1798675200000, y: 13 } ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { show: false }, plotOptions: { calendar: { weekStart: 1 } }, colors: [ 'var(--vt-chart-3)' ] }; ``` ## Compared ### Bar, grouped - - - Amounts side by side. The baseline has to be zero: a bar says how big, and a cut axis lies about it. ```vue template ``` ```ts script const series = [ { name: 'North', data: [ 44, 55, 41, 67 ] }, { name: 'South', data: [ 13, 23, 20, 8 ] }, { name: 'East', data: [ 11, 17, 15, 15 ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { position: 'bottom' }, xaxis: { categories: [ 'Q1', 'Q2', 'Q3', 'Q4' ] } }; ``` ### Bar, stacked - - - The total per category, split. Only the bottom band is easy to compare across categories — put the series that matters there. ```vue template ``` ```ts script const series = [ { name: 'North', data: [ 44, 55, 41, 67 ] }, { name: 'South', data: [ 13, 23, 20, 8 ] }, { name: 'East', data: [ 11, 17, 15, 15 ] } ]; const options = { chart: { stacked: true, toolbar: { show: false } }, legend: { position: 'bottom' }, xaxis: { categories: [ 'Q1', 'Q2', 'Q3', 'Q4' ] } }; ``` ### Bar, to 100% - - - Shares rather than amounts: every bar is full, so the mix is comparable and the size is gone. ```vue template ``` ```ts script const series = [ { name: 'North', data: [ 44, 55, 41, 67 ] }, { name: 'South', data: [ 13, 23, 20, 8 ] }, { name: 'East', data: [ 11, 17, 15, 15 ] } ]; const options = { chart: { stacked: true, stackType: '100%', toolbar: { show: false } }, legend: { position: 'bottom' }, xaxis: { categories: [ 'Q1', 'Q2', 'Q3', 'Q4' ] } }; ``` ### Bar, horizontal Long category names, or many of them: the labels get the room they need and stay level. ```vue template ``` ```ts script const series = [ { name: 'Requests', data: [ 44, 55, 41, 67, 22, 43 ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { show: false }, plotOptions: { bar: { horizontal: true, borderRadius: 4 } }, xaxis: { categories: [ 'Authentication', 'Billing', 'Reporting', 'Search', 'Notifications', 'Exports' ] } }; ``` ### Lollipop A bar with the ink taken out. The same comparison at a fraction of the weight, for a page with many of them. ```vue template ``` ```ts script const series = [ { name: 'Requests', data: [ 44, 55, 41, 67, 22, 43 ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { show: false }, xaxis: { categories: [ 'Authentication', 'Billing', 'Reporting', 'Search', 'Notifications', 'Exports' ], labels: { rotate: -35 } } }; ``` ### Waterfall How a number got from one total to another. Each bar starts where the last one stopped; a total bar returns to zero. ```vue template ``` ```ts script const series = [ { name: 'Cash', data: [ 1200, 420, -180, 310, -260, null ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { show: false }, xaxis: { categories: [ 'Opening', 'Sales', 'Refunds', 'Services', 'Costs', 'Closing' ] }, plotOptions: { waterfall: { totals: [ 5 ] } } }; ``` ### Range bar A bar between two numbers instead of up from zero. Each point is `[low, high]`, so it reads as a span rather than an amount. ```vue template ``` ```ts script const series = [ { name: 'Temperature', data: [ { x: 'Q1', y: [ 8, 17 ] }, { x: 'Q2', y: [ 11, 21 ] }, { x: 'Q3', y: [ 14, 25 ] }, { x: 'Q4', y: [ 17, 29 ] } ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { show: false }, yaxis: { title: { text: '°C' } } }; ``` ### Bullet One measure, its target and the bands it falls in: a gauge’s job in a strip a table row can hold. The tick is the target, the bands are poor, fair and good. ```vue template ``` ```ts script const series = [ { name: 'Against target', data: [ { x: 'Revenue', y: 78, target: 90 }, { x: 'Signups', y: 112, target: 100 }, { x: 'Retention', y: 64, target: 75 }, { x: 'Margin', y: 41, target: 50 } ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { show: false }, plotOptions: { bullet: { ranges: [ { from: 0, to: 130 }, { from: 0, to: 90 }, { from: 0, to: 55 } ] } } }; ``` ## Spread ### Scatter - - Two measures against each other, one point a thing. What it is for is the shape of the cloud, not any one dot. ```vue template ``` ```ts script const series = [ { name: 'Trial', data: [ [ 12, 40 ], [ 18, 52 ], [ 24, 47 ], [ 30, 68 ], [ 36, 71 ], [ 41, 65 ], [ 48, 83 ], [ 54, 78 ] ] }, { name: 'Paid', data: [ [ 15, 22 ], [ 22, 31 ], [ 28, 26 ], [ 34, 44 ], [ 39, 39 ], [ 46, 51 ], [ 52, 48 ], [ 58, 62 ] ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { position: 'bottom' }, xaxis: { type: 'numeric', title: { text: 'Sessions' } }, yaxis: { title: { text: 'Minutes' } } }; ``` ### Bubble - - A scatter with a third number as the area. Area, not radius: doubling the radius would draw four times the quantity. ```vue template ``` ```ts script const series = [ { name: 'Europe', data: [ { x: 12, y: 40, z: 18 }, { x: 24, y: 47, z: 34 }, { x: 36, y: 71, z: 12 }, { x: 48, y: 83, z: 44 } ] }, { name: 'Americas', data: [ { x: 15, y: 22, z: 26 }, { x: 28, y: 26, z: 14 }, { x: 39, y: 39, z: 38 }, { x: 52, y: 48, z: 22 } ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { position: 'bottom' }, xaxis: { type: 'numeric' } }; ``` ### Heat map A grid where colour is the value: two categories in, one measure out. Good for a week by an hour. ```vue template ``` ```ts script const series = [ { name: 'Mon', data: [ { x: '09', y: 8 }, { x: '11', y: 63 }, { x: '13', y: 19 }, { x: '15', y: 74 }, { x: '17', y: 30 }, { x: '19', y: 85 } ] }, { name: 'Tue', data: [ { x: '09', y: 85 }, { x: '11', y: 41 }, { x: '13', y: 96 }, { x: '15', y: 52 }, { x: '17', y: 8 }, { x: '19', y: 63 } ] }, { name: 'Wed', data: [ { x: '09', y: 63 }, { x: '11', y: 19 }, { x: '13', y: 74 }, { x: '15', y: 30 }, { x: '17', y: 85 }, { x: '19', y: 41 } ] }, { name: 'Thu', data: [ { x: '09', y: 41 }, { x: '11', y: 96 }, { x: '13', y: 52 }, { x: '15', y: 8 }, { x: '17', y: 63 }, { x: '19', y: 19 } ] }, { name: 'Fri', data: [ { x: '09', y: 19 }, { x: '11', y: 74 }, { x: '13', y: 30 }, { x: '15', y: 85 }, { x: '17', y: 41 }, { x: '19', y: 96 } ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { show: false } }; ``` ### Histogram Raw readings in, counts out: the chart works out the bins itself. Pass a plain list of numbers, not categories. ```vue template ``` ```ts script const series = [ { name: 'Response time', data: [ 38, 41, 44, 45, 47, 48, 49, 51, 52, 52, 53, 54, 55, 55, 56, 57, 58, 58, 59, 60, 61, 61, 62, 63, 64, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 76, 78, 80, 82, 84, 86, 88, 91, 94, 98, 103, 109, 118, 134 ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { show: false }, xaxis: { title: { text: 'Milliseconds' } }, yaxis: { title: { text: 'Requests' } } }; ``` ### Box plot Five numbers a category — minimum, quartiles, median, maximum — so two distributions compare without either being averaged away. ```vue template ``` ```ts script const series = [ { name: 'Response time', data: [ { x: 'API', y: [ 12, 28, 41, 63, 140 ] }, { x: 'Web', y: [ 30, 52, 68, 90, 180 ] }, { x: 'Jobs', y: [ 8, 15, 22, 34, 70 ] } ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { show: false }, yaxis: { title: { text: 'Milliseconds' } } }; ``` ## Parts of a whole ### Treemap - - - Shares of a total as areas rather than as angles. It holds many more parts than a pie, and the boxes are laid squarely so none of them is a sliver. ```vue template ``` ```ts script const series = [ { name: 'Europe', data: [ { x: 'Germany', y: 84 }, { x: 'France', y: 68 }, { x: 'Spain', y: 47 }, { x: 'Portugal', y: 10 } ] }, { name: 'Americas', data: [ { x: 'United States', y: 132 }, { x: 'Brazil', y: 72 }, { x: 'Mexico', y: 39 }, { x: 'Chile', y: 12 } ] }, { name: 'Asia', data: [ { x: 'Japan', y: 61 }, { x: 'India', y: 55 }, { x: 'Singapore', y: 14 } ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { position: 'bottom' } }; ``` ### Radial bar - - - A ring a measure, as far round as its share of the maximum. Three or four at a time: they are read against their own tracks, not against each other. ```vue template ``` ```ts script const series = [ { name: 'Storage', data: [ 72 ] }, { name: 'Memory', data: [ 48 ] }, { name: 'CPU', data: [ 35 ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { position: 'bottom' } }; ``` ### Gauge One number against its maximum, with the reading in the middle. The arc leaves the bottom open, which is what tells it apart from a ring at a glance. ```vue template ``` ```ts script const series = [ { name: 'Uptime', data: [ 96 ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { position: 'bottom' }, colors: [ 'var(--vt-chart-3)' ], dataLabels: { formatter: '{percent|percent:0}' } }; ``` ### Sunburst A pie of several rings, one a level of a tree. It answers what a pie cannot — what a slice is made of — without leaving the circle. ```vue template ``` ```ts script const series = [ { name: 'Europe', data: [ { x: 'Germany', y: 84 }, { x: 'France', y: 68 }, { x: 'Spain', y: 47 } ] }, { name: 'Americas', data: [ { x: 'United States', y: 132 }, { x: 'Brazil', y: 72 }, { x: 'Mexico', y: 39 } ] }, { name: 'Asia', data: [ { x: 'Japan', y: 61 }, { x: 'India', y: 55 } ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { show: false } }; ``` ### Pie - - - - Shares of one total, at a glance. Three or four slices; past that the angles stop being readable and a bar is kinder. ```vue template ``` ```ts script const series = [ 44, 30, 18, 8 ]; const options = { chart: { toolbar: { show: false } }, legend: { position: 'bottom' }, labels: [ 'Direct', 'Search', 'Social', 'Mail' ] }; ``` ### Donut - - - - A pie with the middle free, which is where the total goes. The hole is what makes it worth using. ```vue template ``` ```ts script const series = [ 44, 30, 18, 8 ]; const options = { chart: { toolbar: { show: false } }, legend: { position: 'bottom' }, labels: [ 'Direct', 'Search', 'Social', 'Mail' ] }; ``` ### Radar - - A profile across the same few measures, for comparing two or three subjects on shape rather than on level. ```vue template ``` ```ts script const series = [ { name: 'This release', data: [ 80, 65, 90, 70, 85, 60 ] }, { name: 'Last release', data: [ 65, 70, 72, 60, 75, 55 ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { position: 'bottom' }, labels: [ 'Speed', 'Size', 'A11y', 'API', 'Docs', 'Tests' ] }; ``` ### Funnel Stages of a process, each as wide as its share of the first. What it is read for is where the drop is, so the labels carry the percentages. ```vue template ``` ```ts script const series = [ { name: 'Signups', data: [ 4820, 3100, 1740, 980, 610 ] } ]; const options = { chart: { toolbar: { show: false } }, legend: { show: false }, labels: [ 'Visited', 'Signed up', 'Activated', 'Subscribed', 'Renewed' ], dataLabels: { enabled: true, formatter: '{value|compact} · {percent|percent:0}' } }; ``` ## Beyond the shapes - **Zoom and brush.** Drag on the plot to zoom, or pair a chart with a brush chart that selects the window. - **Synced groups.** Charts sharing a `group` move their tooltip and their window together. - **Themed by tokens.** Colours, grid and type come from the same tokens as the components, light and dark. - **Without Vue.** `@vitral/chart` mounts into any element; the Vue component is a wrapper over it. [All of it is on the Chart page](http://localhost:4185/vitral/components/chart/) --- # Components > Every component, by what it is for. Each page has live examples, the markup behind them and the API read from the source. Source: https://vitralui.github.io/vitral/components/ Every component, by what it is for. Each page has live examples, the markup behind them and the API read from the source. 105 components ## Form 30 - [**AutoComplete**A text box that suggests as you type, with suggestions supplied by your app, single or multiple values as chips, and a button for the whole list.](http://localhost:4185/vitral/components/autocomplete/) - [**CascadeSelect**A select for nested options, one column per level, opening each group beside the last.](http://localhost:4185/vitral/components/cascadeselect/) - [**Checkbox**A native checkbox under a drawn box, bound to a boolean or to an array of values, with a mixed state.](http://localhost:4185/vitral/components/checkbox/) - [**ColorPicker**A swatch that opens a colour panel, with a value in hex, RGB or HSB and an optional opacity slider.](http://localhost:4185/vitral/components/colorpicker/) - [**DatePicker**A date field you can type into or pick from a calendar, read in the locale's format.](http://localhost:4185/vitral/components/datepicker/) - [**DateRange**Two dates and the days between them, picked on two months side by side or one, with the span drawn before it is committed.](http://localhost:4185/vitral/components/daterange/) - [**Editor**Rich text with its own engine and no dependency: headings, lists, tables, images, links and Markdown shortcuts, bound as sanitised HTML.](http://localhost:4185/vitral/components/editor/) - [**FloatLabel**A real label that starts inside the field and lifts out on focus or once there is a value; IftaLabel keeps it inside, above the value.](http://localhost:4185/vitral/components/floatlabel/) - [**Form**Validation and submission built from parts that wire values, labels, hints and errors to any Vitral control, with rules or a schema.](http://localhost:4185/vitral/components/form/) - [**IconField**An icon inside a field, at the start or the end, with the text padded past it.](http://localhost:4185/vitral/components/iconfield/) - [**InputGroup**Fields, buttons and addons welded into one control, with inner corners squared and a single border between neighbours.](http://localhost:4185/vitral/components/inputgroup/) - [**InputMask**A text box that types into a pattern, such as a phone or a date, filling in the literals for you.](http://localhost:4185/vitral/components/inputmask/) - [**InputNumber**A number box with steppers, clamped to a range and formatted in the locale, as a decimal, a currency or a percentage.](http://localhost:4185/vitral/components/inputnumber/) - [**InputOtp**One box per character of a one-time code, that moves along as you type and fills at once from a paste or autofill.](http://localhost:4185/vitral/components/inputotp/) - [**InputPassword**A password box with a reveal button and a strength meter.](http://localhost:4185/vitral/components/inputpassword/) - [**InputTag**A field whose value is a list of short strings, each shown as a removable tag, added with Enter, a separator or a paste.](http://localhost:4185/vitral/components/inputtag/) - [**InputText**A single-line text box whose attributes land on the native input, with prefix and suffix content and a clear button.](http://localhost:4185/vitral/components/inputtext/) - [**Knob**A dial for a bounded number, set by dragging, pressing anywhere on it or the keyboard.](http://localhost:4185/vitral/components/knob/) - [**Label**A native label with the fields' typography, that checks what it points at and dims when that control is disabled.](http://localhost:4185/vitral/components/label/) - [**MultiSelect**A select for several choices, shown as a list, a count or chips, with an optional filter.](http://localhost:4185/vitral/components/multiselect/) - [**RadioButton**Native radios under drawn rings, grouped by RadioGroup with one name and one v-model.](http://localhost:4185/vitral/components/radiobutton/) - [**Rating**Stars to rate with, in whole steps or in halves and quarters, cleared by choosing the same star again.](http://localhost:4185/vitral/components/rating/) - [**Select**Choose one option from a list, with an optional filter that ignores case and accents.](http://localhost:4185/vitral/components/select/) - [**SelectButton**A segmented control for one choice, as a radio group, or several, as toggle buttons.](http://localhost:4185/vitral/components/selectbutton/) - [**Slider**Pick a value, or a range, by dragging along a track or with the keyboard.](http://localhost:4185/vitral/components/slider/) - [**Textarea**A multi-line text box on the same field chrome as InputText, that can grow with its content.](http://localhost:4185/vitral/components/textarea/) - [**TimePicker**A time of day, typed freely or picked from a list, and kept as minutes past midnight rather than a Date.](http://localhost:4185/vitral/components/timepicker/) - [**ToggleButton**A button that stays pressed, with its own on and off labels and icons.](http://localhost:4185/vitral/components/togglebutton/) - [**ToggleSwitch**An on/off switch over a native checkbox, with a header above and on/off content beside it.](http://localhost:4185/vitral/components/toggleswitch/) - [**TreeSelect**A select over a tree, with the same filter and checkbox selection as Tree.](http://localhost:4185/vitral/components/treeselect/) ## Button 4 - [**Button**A command in eight severities, four variants and three sizes, with icons, a loading state, a badge, and rendering as a link.](http://localhost:4185/vitral/components/button/) - [**ButtonGroup**Buttons that belong together, joined into one named control, each keeping its own tab stop.](http://localhost:4185/vitral/components/buttongroup/) - [**SpeedDial**A floating button that fans its actions out, in a line or around a circle.](http://localhost:4185/vitral/components/speeddial/) - [**SplitButton**A command with a menu of related commands welded to it.](http://localhost:4185/vitral/components/splitbutton/) ## Data 16 - [**Chart**SVG charts with no dependency, over twenty types from one options object, with tooltips, legends, zoom and synced groups, readable from the keyboard.](http://localhost:4185/vitral/components/chart/) - [**DataGrid**A data table that sorts, filters, pages and selects, locally or on your server, with columns the reader can resize, reorder, pin and hide.](http://localhost:4185/vitral/components/datagrid/) - [**DataTable**The small table: rows already in hand, sorted, striped and sized, with a sticky head. DataGrid takes over when it needs more.](http://localhost:4185/vitral/components/datatable/) - [**DataView**Sorting and paging over your own template per item, shown as a list or a grid.](http://localhost:4185/vitral/components/dataview/) - [**Listbox**A list to choose one or several options from, always open: the sibling of Select.](http://localhost:4185/vitral/components/listbox/) - [**OrderList**A list the reader puts in order, with buttons, the keyboard or drag and drop.](http://localhost:4185/vitral/components/orderlist/) - [**OrganizationChart**A tree drawn as a chart, with nodes that fold and open, can be selected, and take a template per node type.](http://localhost:4185/vitral/components/organizationchart/) - [**Paginator**Moves through pages of data, with first, previous, numbered, next and last buttons, and a rows-per-page choice.](http://localhost:4185/vitral/components/paginator/) - [**PickList**Two lists and the moves between them, with buttons, the keyboard or a double click, and reordering within each.](http://localhost:4185/vitral/components/picklist/) - [**Schedule**A calendar with month, week, day, agenda and resource timeline views, drag to move and resize, recurring events and any time zone.](http://localhost:4185/vitral/components/schedule/) - [**Spreadsheet**A spreadsheet with its own formula engine and no dependency: A1 references, sixty-three functions and recalculation of only what changed.](http://localhost:4185/vitral/components/spreadsheet/) - [**Taskboard**A kanban board of cards in columns and swimlanes, dragged by mouse, touch or keyboard, with work-in-progress limits.](http://localhost:4185/vitral/components/taskboard/) - [**Timeline**Events on an axis, down the page or across it, with content on one side, the other, or alternating.](http://localhost:4185/vitral/components/timeline/) - [**Tree**Hierarchical data with branches that open and close, selection by click or checkbox, a filter, and children loaded on demand.](http://localhost:4185/vitral/components/tree/) - [**TreeTable**Rows that expand into rows, with sorting that keeps the nesting and a filter that keeps the branches leading to matches.](http://localhost:4185/vitral/components/treetable/) - [**VirtualScroller**Renders only the items near the viewport, so a hundred thousand rows cost what a screenful does, with lazy loading.](http://localhost:4185/vitral/components/virtualscroller/) ## Panel 9 - [**Accordion**Stacked sections that open and close, one at a time or several at once, with headers that follow the WAI-ARIA accordion.](http://localhost:4185/vitral/components/accordion/) - [**Card**A container for one piece of content: an optional header image, a title and subtitle, the content and a footer of actions.](http://localhost:4185/vitral/components/card/) - [**Divider**A horizontal or vertical line between pieces of content, solid, dashed or dotted, with optional content set into it.](http://localhost:4185/vitral/components/divider/) - [**Fieldset**A native fieldset that names the controls inside it, and can collapse behind its legend.](http://localhost:4185/vitral/components/fieldset/) - [**Panel**A titled container that can collapse behind its header.](http://localhost:4185/vitral/components/panel/) - [**ScrollPanel**A scroll container with the theme's own thin bars, over native scrolling.](http://localhost:4185/vitral/components/scrollpanel/) - [**Stepper**Steps and their panels, horizontal or vertical, with an optional linear mode that keeps later steps out of reach.](http://localhost:4185/vitral/components/stepper/) - [**Tabs**Views that share one place on the page, switched by a list of tabs.](http://localhost:4185/vitral/components/tabs/) - [**Toolbar**A row of controls in start, center and end groups, with optional arrow-key navigation between them.](http://localhost:4185/vitral/components/toolbar/) ## Overlay 8 - [**ConfirmDialog**A modal question before an action, opened from anywhere with useConfirm, that can default to the safe answer.](http://localhost:4185/vitral/components/confirmdialog/) - [**ConfirmPopup**A confirmation anchored to the element it is about, opened with useConfirm and holding focus until answered.](http://localhost:4185/vitral/components/confirmpopup/) - [**Dialog**A window over the page, modal or not, that takes focus in, keeps it inside and returns it when it closes.](http://localhost:4185/vitral/components/dialog/) - [**Drawer**A panel that slides in from an edge of the viewport, or covers it, with the same modal behaviour as Dialog.](http://localhost:4185/vitral/components/drawer/) - [**DynamicDialog**A dialog opened from code with a component as its content, which receives data and closes with a result.](http://localhost:4185/vitral/components/dynamicdialog/) - [**HoverCard**A preview of what a link points at, shown while the pointer rests on it or focus reaches it.](http://localhost:4185/vitral/components/hovercard/) - [**Popover**A non-modal dialog anchored to what opened it, closing on Escape, a press outside or tabbing out.](http://localhost:4185/vitral/components/popover/) - [**Tooltip**The v-tooltip directive: a hint shown on hover and on keyboard focus, which describes the element it sits on.](http://localhost:4185/vitral/components/tooltip/) ## Menu 10 - [**Breadcrumb**Where the page sits in a hierarchy: a trail of links with the current page last and separators in between.](http://localhost:4185/vitral/components/breadcrumb/) - [**Command**A search box over a list of commands that filters as you type, inline or as a modal palette opened with a hotkey.](http://localhost:4185/vitral/components/command/) - [**ContextMenu**The menu a right-click or the context-menu key opens, at the pointer or under the focused element, with submenus.](http://localhost:4185/vitral/components/contextmenu/) - [**Dock**A strip of large icons that grow under the pointer, each labelled with a tooltip.](http://localhost:4185/vitral/components/dock/) - [**MegaMenu**A menubar whose items open wide panels of grouped links in columns, like this site's own top bar.](http://localhost:4185/vitral/components/megamenu/) - [**Menu**A list of commands and links, inline or as a popup, with groups and separators.](http://localhost:4185/vitral/components/menu/) - [**Menubar**The application menu bar, with nested menus, that becomes a single menu button on narrow screens.](http://localhost:4185/vitral/components/menubar/) - [**PanelMenu**Sections of navigation that expand in place, each a tree of its items, for a sidebar.](http://localhost:4185/vitral/components/panelmenu/) - [**Sidebar**The collapsible application sidebar: navigation with sub-lists, that shrinks to its icons and becomes a drawer on small screens.](http://localhost:4185/vitral/components/sidebar/) - [**TieredMenu**A menu whose items open submenus beside them, to any depth, inline or as a popup.](http://localhost:4185/vitral/components/tieredmenu/) ## Messages 8 - [**BlockUI**A veil over a region, or the whole page, while it is busy, making what is under it inert and saying why.](http://localhost:4185/vitral/components/blockui/) - [**Chat**A conversation with no dependency, in five shapes from a plain thread to an agent with its tool calls, with answers that stream in place.](http://localhost:4185/vitral/components/chat/) - [**Message**An inline message with a title, an action and a close button, in every severity.](http://localhost:4185/vitral/components/message/) - [**MeterGroup**Several values on one bar, with a legend, read out as a single total.](http://localhost:4185/vitral/components/metergroup/) - [**ProgressBar**How far along a task is, as a thin bar, with a value or indeterminate.](http://localhost:4185/vitral/components/progressbar/) - [**ProgressSpinner**A ring that turns while something is running, or fills clockwise to a value.](http://localhost:4185/vitral/components/progressspinner/) - [**Skeleton**The outline of content that is still loading, in any shape, from a line of text to a whole card.](http://localhost:4185/vitral/components/skeleton/) - [**Toast**Notifications sent from anywhere with useToast, stacked at a chosen position, that pause their timer while hovered or focused.](http://localhost:4185/vitral/components/toast/) ## Media 7 - [**AspectRatio**A box that keeps its ratio as its width changes, with the content cropped to fill it.](http://localhost:4185/vitral/components/aspectratio/) - [**Avatar**A face, initials or an icon in a fixed box, in three sizes and with an optional badge; AvatarGroup overlaps a row of them.](http://localhost:4185/vitral/components/avatar/) - [**Carousel**A track of items with paging and optional rotation, which pauses on demand and while you point at or work in it.](http://localhost:4185/vitral/components/carousel/) - [**Cropper**A crop rectangle over an image, with no dependency: lock a ratio, mask a circle, rotate and flip, and crop on a server or in the browser.](http://localhost:4185/vitral/components/cropper/) - [**FileUpload**A file input drawn as a button, a drop zone and a queue, checking type, size and count, with upload progress.](http://localhost:4185/vitral/components/fileupload/) - [**Galleria**Images with thumbnails, indicators, rotation and a full-screen mode.](http://localhost:4185/vitral/components/galleria/) - [**Image**An image that can open in a viewer over the page, with rotate and zoom.](http://localhost:4185/vitral/components/image/) ## Layout 7 - [**DockPanel**Docks regions to its top, bottom, left and right edges and gives what is left to the fill; the order decides who wins the corners.](http://localhost:4185/vitral/components/dockpanel/) - [**Grid**A grid with star sizing: rows and columns sized to content, as shares of what is left, or in pixels, with children placed by row and column.](http://localhost:4185/vitral/components/grid/) - [**Splitter**Panels with a gutter between each pair that resizes them, by dragging or from the keyboard, within each panel's minimum.](http://localhost:4185/vitral/components/splitter/) - [**SplitView**A pane beside the content, inline or as an overlay, that can shrink to a strip of icons when closed.](http://localhost:4185/vitral/components/splitview/) - [**StackPanel**Arranges its children in one line, stacked or side by side, with even spacing between them.](http://localhost:4185/vitral/components/stackpanel/) - [**UniformGrid**A grid of equal cells that works out its rows or columns from the number of children.](http://localhost:4185/vitral/components/uniformgrid/) - [**WrapPanel**Lays children out in a line and wraps to the next when it is full, across or down, with an optional uniform size.](http://localhost:4185/vitral/components/wrappanel/) ## Misc 6 - [**Badge**A count or a status dot, on its own or pinned to the corner of what it wraps with OverlayBadge.](http://localhost:4185/vitral/components/badge/) - [**Chip**A compact token for a filter, a recipient or a tag, with an optional image, icon and remove button.](http://localhost:4185/vitral/components/chip/) - [**Inplace**A display that turns into its editor when activated, and back again when done.](http://localhost:4185/vitral/components/inplace/) - [**ScrollTop**A button back to the top of a long page or a scrolling element, shown past a threshold.](http://localhost:4185/vitral/components/scrolltop/) - [**Tag**A short label for a status or a category, in every severity, with an optional remove button.](http://localhost:4185/vitral/components/tag/) - [**Terminal**A prompt and a log, for a console inside an application, with a command history.](http://localhost:4185/vitral/components/terminal/) --- # Templates > Multi-screen applications built only from Vitral components, themed by the same tokens. Each one opens as a live demo, with its source on GitHub. Source: https://vitralui.github.io/vitral/templates/ 10 multi-screen applications built only from Vitral components: a store, a newsroom, dashboards, a booking flow. Each one is responsive, follows whichever theme you pick, and its source is one click away. - **[Fernhill Supply](http://localhost:4185/vitral/templates/ecommerce/)**: Commerce · 4 screens[Fernhill Supply](http://localhost:4185/vitral/templates/ecommerce/)An online store: catalogue, product page, cart and a three-step checkout.StoreCheckoutCatalogue[Details](http://localhost:4185/vitral/templates/ecommerce/)[Live preview](http://localhost:4185/vitral/templates/ecommerce/preview/) - **[Lumen Metrics](http://localhost:4185/vitral/templates/saas/)**: Dashboard · 3 screens[Lumen Metrics](http://localhost:4185/vitral/templates/saas/)A SaaS admin: revenue KPIs and charts, a customer table and settings.AdminChartsTable[Details](http://localhost:4185/vitral/templates/saas/)[Live preview](http://localhost:4185/vitral/templates/saas/preview/) - **[The Lantern](http://localhost:4185/vitral/templates/news/)**: Publishing · 3 screens[The Lantern](http://localhost:4185/vitral/templates/news/)A news site: a front page, an article page and paginated sections.NewsEditorialContent[Details](http://localhost:4185/vitral/templates/news/)[Live preview](http://localhost:4185/vitral/templates/news/preview/) - **[Field Notes](http://localhost:4185/vitral/templates/blog/)**: Publishing · 3 screens[Field Notes](http://localhost:4185/vitral/templates/blog/)A personal blog: posts with tags, a post page with comments, and an about page.BlogPersonalContent[Details](http://localhost:4185/vitral/templates/blog/)[Live preview](http://localhost:4185/vitral/templates/blog/preview/) - **[Northgate Homes](http://localhost:4185/vitral/templates/realestate/)**: Commerce · 3 screens[Northgate Homes](http://localhost:4185/vitral/templates/realestate/)A property site: search with a map, filtered listings and a home page with visit booking.Real estateSearchBooking[Details](http://localhost:4185/vitral/templates/realestate/)[Live preview](http://localhost:4185/vitral/templates/realestate/preview/) - **[Skylark Air](http://localhost:4185/vitral/templates/airline/)**: Travel · 4 screens[Skylark Air](http://localhost:4185/vitral/templates/airline/)A flight booking flow: search, results, seat selection and a verified payment.BookingTravelCheckout[Details](http://localhost:4185/vitral/templates/airline/)[Live preview](http://localhost:4185/vitral/templates/airline/preview/) - **[Brightpath Academy](http://localhost:4185/vitral/templates/lms/)**: Education · 4 screens[Brightpath Academy](http://localhost:4185/vitral/templates/lms/)A learning platform: courses, a video lesson, a class calendar and grades.EducationVideoCalendar[Details](http://localhost:4185/vitral/templates/lms/)[Live preview](http://localhost:4185/vitral/templates/lms/preview/) - **[Harborline Bank](http://localhost:4185/vitral/templates/finance/)**: Dashboard · 3 screens[Harborline Bank](http://localhost:4185/vitral/templates/finance/)Online banking: account balances with charts, a transactions table and a transfer flow.BankingChartsForms[Details](http://localhost:4185/vitral/templates/finance/)[Live preview](http://localhost:4185/vitral/templates/finance/preview/) - **[Orbit](http://localhost:4185/vitral/templates/projects/)**: Productivity · 3 screens[Orbit](http://localhost:4185/vitral/templates/projects/)Project management: a kanban board, a team timeline and workload.KanbanTimelineTeam[Details](http://localhost:4185/vitral/templates/projects/)[Live preview](http://localhost:4185/vitral/templates/projects/preview/) - **[Meridian Access](http://localhost:4185/vitral/templates/auth/)**: Authentication · 5 screens[Meridian Access](http://localhost:4185/vitral/templates/auth/)The screens before the application: sign in on a card, beside a panel or in a dialog, sign up, and reset a password.AuthFormsDialog[Details](http://localhost:4185/vitral/templates/auth/)[Live preview](http://localhost:4185/vitral/templates/auth/preview/) ## Make one your own A template is a folder of Vue files over the same tokens as everything else, so changing the preset changes it too. --- # Page not found > Nothing is published at this address. Source: https://vitralui.github.io/vitral/404/ 404 # This page does not exist Nothing is published at `/vitral/404`. The link may be out of date, or the address may have a typo in it. - [**Components**The whole set, by category](http://localhost:4185/vitral/components/) - [**Documentation**Installing, theming, customising](http://localhost:4185/vitral/docs/introduction/) - [**Templates**Whole applications, live](http://localhost:4185/vitral/templates/) - [**Icons**The icon set, searchable](http://localhost:4185/vitral/icons/) --- # Accessibility > A condition of a component existing, not a milestone on its way there. From the documentation of Vitral, a Vue 3 component library. Source: https://vitralui.github.io/vitral/docs/accessibility/ A condition of a component existing, not a milestone on its way there. A component is not done until its accessibility works: the role, an accessible name, every state and value that applies, the relations that tie it to its label, description and popup, and the keyboard behaviour of the matching [WAI-ARIA APG pattern](https://www.w3.org/WAI/ARIA/apg/patterns/). ## What every spec does - Runs **axe** over the representative states, and for an overlay both closed and open. - Drives the **keyboard** through the pattern's keys and asserts what changed. - Asserts the **ARIA attributes**, not only the classes: a test that only sees classes cannot tell a styled div from a control. ```ts Select.spec.ts it('is a combobox that answers the keyboard', async () => { const wrapper = mount(Select, { props: { options, optionLabel: 'name' } }); await expectNoA11yViolations(wrapper); // axe, closed await press(wrapper, 'ArrowDown'); // opens and marks the first option await expectNoA11yViolations(wrapper); // axe, open expect(trigger.attributes('aria-expanded')).toBe('true'); expect(trigger.attributes('aria-activedescendant')).toBe(optionId(0)); }); ``` ## What that means in practice - Fields are real controls: `