Skip to content
CSS-first · No frameworks

One visual foundation.
Every component.

Build interfaces with native HTML, VEXARO red and an API that grows with your project.

Get started View dashboard Explore icons

Introduction

VEXARO UI is a CSS-first library with no frameworks. The default identity uses red, black and white, with square corners on all components. Green and amber indicate success and warning.

Use semantic HTML, native CSS and JavaScript only for interaction. Version 1.0.0 delivers every component from VEX-UI-001 through VEX-UI-018, including advanced controls and optional integration.

Target compatibility: current Chrome, Firefox, Edge and Safari versions with CSS Grid, dialog.showModal(), inert and ES Modules. Datepicker uses the browser's native calendar; appearance and shortcuts vary by platform. See the public contract and the changes.

Quick Start

Copy the two files from dist/ into your project. They are independent of the sources. On a static server, use:

<!doctype html>
<html lang="en" data-vx-theme="light">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://ui.vexaro.solutions/dist/vexaro.css">
  <script type="module" src="https://ui.vexaro.solutions/dist/vexaro.js"></script>
  <title>My VEXARO project</title>
</head>
<body>
  <main class="vx-container vx-py-8">
    <h1>Meu projeto</h1>
    <button class="vx-btn" type="button" data-vx-toggle="toast"
      data-vx-message="Ready!">Test notification</button>
  </main>
</body>
</html>

To open directly via file://, or to integrate with classic scripts, replace the script with:

<script defer src="https://ui.vexaro.solutions/dist/vexaro.global.js"></script>

The classic bundle exposes window.Vexaro. Both versions initialize the attributes data-vx-* automatically. Load only one of them. Alternatively, use css/vexaro.css and js/vexaro.js, preserving the source folders.

The reset/reboot is global: it normalizes box-sizing, margins, controls, typography and focus. Components, utilities and tokens use the prefix vx-.

Design tokens

Semantic tokens follow the theme. The primary red is #b91c1c in light mode and #ff3333 in dark mode. Use --vx-color-primary instead of fixing a primitive color. All border radii are 0 by default.

Primitive colors

TokenValuePreview
--vx-white#ffffff
--vx-black#000000
--vx-neutral-50#fafafa
--vx-neutral-100#f5f5f5
--vx-neutral-200#e5e5e5
--vx-neutral-400#a3a3a3
--vx-neutral-500#737373
--vx-neutral-600#525252
--vx-neutral-900#171717
--vx-red-400#f87171
--vx-red-500#ff3333
--vx-red-700#b91c1c
--vx-slate-50#f8fafc
--vx-slate-100#f1f5f9
--vx-slate-200#e2e8f0
--vx-slate-400#94a3b8
--vx-slate-500#64748b
--vx-slate-600#475569
--vx-slate-800#1e293b
--vx-slate-900#0f172a
--vx-blue-300#93c5fd
--vx-blue-700#1d4ed8
--vx-green-300#86efac
--vx-green-800#166534
--vx-amber-300#fcd34d
--vx-amber-900#78350f
--vx-red-300#fca5a5
--vx-red-800#991b1b

Spacing

TokenValuePreview
--vx-space-00
--vx-space-10.25rem
--vx-space-20.5rem
--vx-space-30.75rem
--vx-space-41rem
--vx-space-51.25rem
--vx-space-61.5rem
--vx-space-82rem
--vx-space-102.5rem
--vx-space-123rem
--vx-space-164rem

Typography

TokenValuePreview
--vx-font-sanssystem-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif
--vx-font-monoui-monospace, SFMono-Regular, Consolas, monospace
--vx-text-xs0.75rem
--vx-text-sm0.875rem
--vx-text-md1rem
--vx-text-lg1.125rem
--vx-text-xl1.25rem
--vx-text-2xl1.5rem
--vx-text-3xl2rem
--vx-text-4xl2.5rem
--vx-font-normal400
--vx-font-medium500
--vx-font-semibold600
--vx-font-bold700
--vx-leading-tight1.25
--vx-leading-normal1.5
--vx-leading-relaxed1.75

Borders and focus

TokenValuePreview
--vx-border-width1px
--vx-border-width-thick2px
--vx-radius-none0
--vx-radius-sm0
--vx-radius-md0
--vx-radius-lg0
--vx-radius-pill0
--vx-focus-width3px
--vx-focus-offset3px

Shadows

TokenValuePreview
--vx-shadow-nonenone
--vx-shadow-sm0 1px 3px rgb(0 0 0 / 0.12)
--vx-shadow-md0 4px 12px rgb(0 0 0 / 0.16)
--vx-shadow-lg0 12px 32px rgb(0 0 0 / 0.2)

Semantic colors

TokenLightDark
--vx-color-bgvar(--vx-white)var(--vx-black)
--vx-color-surfacevar(--vx-white)var(--vx-neutral-900)
--vx-color-surface-mutedvar(--vx-neutral-100)var(--vx-black)
--vx-color-textvar(--vx-black)var(--vx-white)
--vx-color-text-mutedvar(--vx-neutral-600)var(--vx-neutral-200)
--vx-color-bordervar(--vx-neutral-500)var(--vx-neutral-400)
--vx-color-border-subtlevar(--vx-neutral-200)var(--vx-neutral-600)
--vx-color-primaryvar(--vx-red-700)var(--vx-red-500)
--vx-color-on-primaryvar(--vx-white)var(--vx-black)
--vx-color-successvar(--vx-green-800)var(--vx-green-300)
--vx-color-on-successvar(--vx-white)var(--vx-black)
--vx-color-warningvar(--vx-amber-900)var(--vx-amber-300)
--vx-color-on-warningvar(--vx-white)var(--vx-black)
--vx-color-dangervar(--vx-red-800)var(--vx-red-300)
--vx-color-on-dangervar(--vx-white)var(--vx-black)
--vx-color-focusvar(--vx-red-700)var(--vx-red-500)

Themes

Light is the default theme. Set the attribute on HTML or a container. Local themes keep their colors when the page theme changes.

JavaScript: no

Light

Red, black and white.

Dark

Bold red on black.

<div class="vx-grid">
  <article class="vx-card vx-col-12 vx-col-md-6" data-vx-theme="light">
    <div class="vx-card-body"><h3>Light</h3><p>Red, black and white.</p><button class="vx-btn" type="button">Primary action</button></div>
  </article>
  <article class="vx-card vx-col-12 vx-col-md-6" data-vx-theme="dark">
    <div class="vx-card-body"><h3>Dark</h3><p>Bold red on black.</p><button class="vx-btn" type="button">Primary action</button></div>
  </article>
</div>
Class / attributeUsage
data-vx-theme="light|dark"Global or local theme; works without JS.
data-vx-toggle="theme"Toggle button; requires JS.

Customization

Load your CSS after the library and override color pairs together. Explicit themes reset colors in their own scope; customize each required selector.

:root, [data-vx-theme="light"] {
  --vx-color-primary: #a31515;
  --vx-color-on-primary: #ffffff;
}
[data-vx-theme="dark"] {
  --vx-color-primary: #ff3333;
  --vx-color-on-primary: #000000;
}

Blue and slate primitives remain available for compatibility; the default identity uses red and neutrals. Palette changes require contrast revalidation. Scales in rem follow the root font size.

Container and grid

CSS Grid for two-dimensional layouts. Start with 12 columns on mobile and override spans at larger breakpoints.

JavaScript: no

4 columns on tablets
8 columns on tablets
<div class="vx-container-fluid">
  <div class="vx-grid vx-gap-4">
    <div class="vx-card vx-col-12 vx-col-md-4 vx-p-4">4 columns on tablets</div>
    <div class="vx-card vx-col-12 vx-col-md-8 vx-p-4">8 columns on tablets</div>
  </div>
</div>
Class / attributeUsage
vx-container / vx-container-fluidResponsive maximum width / full width, with side padding.
vx-grid / vx-grid-cols-1212-column grid; default gap is space-4.
vx-col-1 … vx-col-12Number of occupied columns.
vx-col-{sm|md|lg|xl|xxl}-{1…12}Spans by minimum breakpoint.
Breakpointssm 576px · md 768px · lg 992px · xl 1200px · xxl 1400px
Containers540 / 720 / 960 / 1140 / 1320px at the corresponding breakpoints.

Flexbox

Use Flexbox for action bars and one-dimensional alignment.

JavaScript: no

3 changes saved
<div class="vx-flex vx-flex-wrap vx-items-center vx-justify-between vx-gap-4 vx-w-full">
  <span class="vx-badge vx-badge-success">3 changes saved</span>
  <div class="vx-flex vx-gap-2"><button type="button" class="vx-btn vx-btn-secondary">Cancel</button><button type="button" class="vx-btn">Save</button></div>
</div>
Class / attributeUsage
vx-flex / vx-flex-row / vx-flex-colDisplay and direction.
vx-flex-wrap / vx-flex-nowrap / vx-flex-1 / vx-flex-noneWrapping and growth.
vx-justify-start|end|center|between|aroundMain-axis alignment.
vx-items-start|end|center|stretch / vx-self-centerCross-axis alignment.

Buttons

Use button for actions and links for navigation. Visible labels explain the action.

JavaScript: no

<button class="vx-btn vx-btn-primary" type="button">Primary</button>
<button class="vx-btn vx-btn-secondary" type="button">Secondary</button>
<button class="vx-btn vx-btn-outline" type="button">Outline</button>
<button class="vx-btn vx-btn-ghost" type="button">Ghost</button>
<button class="vx-btn vx-btn-success" type="button">Confirm</button>
<button class="vx-btn vx-btn-danger" type="button">Delete</button>
<button class="vx-btn vx-btn-sm" type="button">Small</button>
<button class="vx-btn vx-btn-lg" type="button">Large</button>
<button class="vx-btn vx-btn-rounded" type="button">Rounded</button>
<button class="vx-btn vx-btn-pill" type="button">Pill</button>
<button class="vx-btn vx-btn-outline vx-btn-pill" type="button">Outline pill</button>
<button class="vx-btn" type="button" disabled>Unavailable</button>
<button class="vx-btn" type="button" aria-busy="true" disabled>Saving…</button>
Class / attributeUsage
vx-btnBase; medium size.
vx-btn-primary|secondary|outline|ghost|success|dangerSemantic variants.
vx-btn-sm / vx-btn-lgSizes.
vx-btn-rounded / vx-btn-pillRounded corners (.5rem) or a pill shape; works with any variant.
disabled / aria-disabled="true"Native blocking / semantics. VEXARO JS prevents clicks on aria-disabled; without JS, your application must prevent link activation.
aria-busy="true"Communicates loading state to assistive technology. Also update the text to “Saving…” and use disabled to block repeated actions.
--vx-color-primary / --vx-radius-mdPrimary color and radius (0 by default).

Buttons with Vexaro icons

Use SVGs from the icon catalog inside the button. The stroke inherits text color through currentColor. Icons alongside text are decorative; icon-only buttons need an accessible name in aria-label.

<button class="vx-btn" type="button">
  <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M3 3h14l4 4v14H3Z"/><path d="M7 3v6h9V3"/><path d="M7 21v-7h10v7"/></svg>
  Save
</button>
<button class="vx-btn vx-btn-secondary" type="button">
  <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M12 3v12"/><path d="m7 10 5 5 5-5"/><path d="M4 16v5h16v-5"/></svg>
  Download
</button>
<button class="vx-btn vx-btn-outline" type="button">
  <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M12 4v16"/><path d="M4 12h16"/></svg>
  Add
</button>
<button class="vx-btn vx-btn-danger" type="button">
  <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M3 6h18"/><path d="M9 6V3h6v3"/><path d="m5 6 1 15h12l1-15"/><path d="M10 10v7"/><path d="M14 10v7"/></svg>
  Delete
</button>
<button class="vx-btn vx-btn-secondary" type="button" aria-label="Settings">
  <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M9 3h6l1 4 4 1v8l-4 1-1 4H9l-1-4-4-1V8l4-1Z"/><path d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"/></svg>
</button>

Dashboards

Reusable compositions made only with VEXARO UI components. Choose one of 10 models, copy the HTML and connect your data.

Composition with public classes

Infrastructure

Production capacity

Servers

12

3 active regions

Availability

99.98%

Last 30 days

68%
<div class="vx-dashboard">
  <header class="vx-dashboard-header">
    <div><h3 class="vx-dashboard-title">Infrastructure</h3><p class="vx-dashboard-subtitle">Production capacity</p></div>
    <button class="vx-btn" type="button">New server</button>
  </header>
  <div class="vx-dashboard-metrics">
    <article class="vx-card vx-stat"><h3 class="vx-stat-label">Servers</h3><strong class="vx-stat-value">12</strong><p class="vx-stat-description">3 active regions</p></article>
    <article class="vx-card vx-stat"><h3 class="vx-stat-label">Availability</h3><strong class="vx-stat-value">99.98%</strong><p class="vx-stat-description">Last 30 days</p></article>
  </div>
  <section class="vx-card"><div class="vx-card-body vx-progress-group"><label for="dashboard-ram">RAM · 68%</label><progress class="vx-progress" id="dashboard-ram" value="68" max="100">68%</progress></div></section>
</div>

Load only https://ui.vexaro.solutions/dist/vexaro.css and, for modal, tabs and table, https://ui.vexaro.solutions/dist/vexaro.global.js. The models share demo JavaScript only for exports and forms; no local CSS is needed.

Use vx-dashboard-split, vx-dashboard-equal or vx-dashboard-reverse to organize panels. The grid adapts to the available width. The variant vx-theme-black in <html data-vx-theme="dark"> applies deep black and subtle decorative borders without changing the light theme.

10 complete models

Progress bars

Native progress, with known or indeterminate value. No JavaScript required.

68%
100% 80% 95%
<div class="vx-w-full vx-dashboard-stack">
  <div class="vx-progress-group">
    <label class="vx-progress-label" for="demo-progress"><span>RAM</span><span>68%</span></label>
    <progress class="vx-progress" id="demo-progress" value="68" max="100">68%</progress>
  </div>
  <progress class="vx-progress vx-progress-success vx-progress-sm" value="100" max="100" aria-label="Backup complete">100%</progress>
  <progress class="vx-progress vx-progress-warning" value="80" max="100" aria-label="Storage used: 80%">80%</progress>
  <progress class="vx-progress vx-progress-danger vx-progress-lg" value="95" max="100" aria-label="CPU usage: 95%">95%</progress>
  <progress class="vx-progress" aria-label="Importing files"></progress>
</div>

vx-progress: base; vx-progress-sm|lg: thickness; vx-progress-success|warning|danger: state. Use value and max; omit value when the duration is unknown. Always provide a label or aria-label. Adjust the color with the token --vx-progress-color. Animations respect reduced motion.

Preloaders

Explicit loading indicators: spinner, dots and skeleton. They are not added to buttons automatically.

Loading data…
Connecting to the server…
<div class="vx-dashboard-stack vx-w-full">
  <div class="vx-flex vx-items-center vx-gap-3" role="status">
    <span class="vx-preloader" aria-hidden="true"></span><span>Loading data…</span>
  </div>
  <div class="vx-flex vx-items-center vx-gap-3" role="status">
    <span class="vx-preloader vx-preloader-dots" aria-hidden="true"><span></span><span></span><span></span></span><span>Connecting to the server…</span>
  </div>
  <div class="vx-card vx-w-full" aria-busy="true" aria-label="Loading summary">
    <div class="vx-card-body vx-dashboard-stack" aria-hidden="true">
      <span class="vx-skeleton vx-skeleton-avatar"></span>
      <span class="vx-skeleton vx-skeleton-title"></span>
      <span class="vx-skeleton vx-skeleton-text"></span>
      <span class="vx-skeleton vx-skeleton-text"></span>
    </div>
  </div>
</div>

vx-preloader: spinner; vx-preloader-sm|lg: size; vx-preloader-dots: three dots; vx-skeleton and variants title|text|avatar: temporary structure. Use text in role="status" and mark the content awaiting an update with aria-busy="true". Once complete, remove the indicators and set aria-busy to false. No animation when the user prefers reduced motion.

Checklist

Task list with native checkboxes, optional descriptions and completed states. Works with keyboard without JavaScript.

<ul class="vx-checklist vx-w-full">
  <li><label class="vx-checklist-item"><input class="vx-checkbox" type="checkbox" checked><span class="vx-checklist-content"><span class="vx-checklist-title">Validate daily backup</span><span class="vx-checklist-description">Infrastructure · complete</span></span><span class="vx-badge vx-badge-success">Done</span></label></li>
  <li><label class="vx-checklist-item"><input class="vx-checkbox" type="checkbox"><span class="vx-checklist-content"><span class="vx-checklist-title">Review permissions</span><span class="vx-checklist-description">Security · today</span></span><span class="vx-badge">High</span></label></li>
  <li><label class="vx-checklist-item"><input class="vx-checkbox" type="checkbox" disabled><span class="vx-checklist-content"><span class="vx-checklist-title">Publish release</span><span class="vx-checklist-description">Awaiting review</span></span></label></li>
</ul>

vx-checklist: list; vx-checklist-item: clickable label; vx-checklist-content, vx-checklist-title and vx-checklist-description: content. The visual state follows checked; disabled prevents changes. Persistence and business rules belong to your application.

Switch

The on/off control already available in the library, now with its own section, examples and documented states.

Notification preferences
<fieldset class="vx-w-full">
  <legend>Notification preferences</legend>
  <label class="vx-check"><input class="vx-switch" type="checkbox" role="switch" checked>Incident alerts</label>
  <label class="vx-check"><input class="vx-switch" type="checkbox" role="switch">Weekly report</label>
  <label class="vx-check"><input class="vx-switch" type="checkbox" role="switch" disabled>Integration unavailable</label>
  <label class="vx-check"><input class="vx-switch" type="checkbox" role="switch" checked disabled>Required policy</label>
</fieldset>

Use input type="checkbox" role="switch" class="vx-switch" inside a label. checked sets the on state; disabled prevents changes. Tab moves focus and Space toggles the state. Read input.checked on the event change. Do not duplicate the state with aria-checked on a native input.

Theme switch with icons

The component vx-theme-switch uses Vexaro sun and moon icons and keeps a rectangular shape. The state aria-checked="true" indicates dark theme; the UI JavaScript synchronizes the control with the theme. Enter and Space toggle the state.

<button class="vx-theme-switch" type="button" role="switch" aria-checked="false" aria-label="Dark theme" data-vx-toggle="theme">
  <svg class="vx-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m5 5 1.5 1.5"/><path d="m17.5 17.5 1.5 1.5"/><path d="m5 19 1.5-1.5"/><path d="m17.5 6.5 1.5-1.5"/></svg>
  <svg class="vx-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 14A9 9 0 0 1 10 3a8 8 0 0 0 11 11Z"/></svg>
</button>

Badges

Short labels for categories and states. Information also appears in the text.

JavaScript: no

Draft Under review Published Pending Failed Count: 12
<span class="vx-badge">Draft</span>
<span class="vx-badge vx-badge-primary">Under review</span>
<span class="vx-badge vx-badge-success">Published</span>
<span class="vx-badge vx-badge-warning">Pending</span>
<span class="vx-badge vx-badge-danger">Failed</span>
<span class="vx-badge vx-badge-lg">Count: 12</span>
Class / attributeUsage
vx-badgeNeutral base; static.
vx-badge-primary|success|warning|dangerContextual color.
vx-badge-lgLarger size.

Cards

Group content with optional header, body and footer.

JavaScript: no

VEXARO project

Active

A shared foundation for applications and documentation.

<article class="vx-card vx-card-elevated">
  <header class="vx-card-header"><h3>VEXARO project</h3></header>
  <div class="vx-card-body"><span class="vx-badge vx-badge-success">Active</span><p class="vx-mt-4">A shared foundation for applications and documentation.</p></div>
  <footer class="vx-card-footer"><a class="vx-btn" href="#quick-start">Start project</a></footer>
</article>
Class / attributeUsage
vx-card / vx-card-header|body|footerSurface and structure.
vx-card-elevated / vx-card-borderless / vx-card-outlinedShadow, borderless or thick border.
--vx-color-surface / --vx-shadow-md / --vx-radius-lgAppearance tokens.

Alerts

Static feedback needs no live region. For dynamic updates, use role="status" for routine messages and role="alert" only for urgency.

JavaScript: no

Information: your session is active.
Success: changes saved.
Warning: check the fields.
Error: could not save.
<div class="vx-alert vx-alert-info"><strong>Information:</strong> your session is active.</div>
<div class="vx-alert vx-alert-success"><strong>Success:</strong> changes saved.</div>
<div class="vx-alert vx-alert-warning"><strong>Warning:</strong> check the fields.</div>
<div class="vx-alert vx-alert-danger"><strong>Error:</strong> could not save.</div>
Class / attributeUsage
vx-alertMessage with padding and border.
vx-alert-info|success|warning|dangerVariants combine contextual color and the corresponding on-* color.

Forms

Native controls with associated labels and descriptions. HTML5 validation still works without JS.

JavaScript: no

Enter your full name.
Preferences
Use a six-digit code.
<form class="vx-w-full" data-docs-form>
  <div class="vx-field"><label class="vx-label" for="demo-name">Name</label><input class="vx-input" id="demo-name" name="name" required autocomplete="name" aria-describedby="name-help"><span class="vx-help" id="name-help">Enter your full name.</span></div>
  <div class="vx-field"><label class="vx-label" for="demo-email">E-mail</label><input class="vx-input" type="email" id="demo-email" name="email" required autocomplete="email"></div>
  <div class="vx-field"><label class="vx-label" for="demo-role">Profile</label><select class="vx-select" id="demo-role" name="role"><option>Designer</option><option>Developer</option></select></div>
  <div class="vx-field"><label class="vx-label" for="demo-message">Message</label><textarea class="vx-textarea" id="demo-message" name="message"></textarea></div>
  <fieldset class="vx-p-4 vx-mb-4"><legend>Preferences</legend>
    <label class="vx-check"><input class="vx-checkbox" type="checkbox" name="news"> Receive updates</label>
    <label class="vx-check"><input class="vx-radio" type="radio" name="plan" value="personal" checked> Personal</label>
    <label class="vx-check"><input class="vx-radio" type="radio" name="plan" value="team"> Team</label>
    <label class="vx-check"><input class="vx-switch" type="checkbox" role="switch" name="notifications"> Notifications</label>
  </fieldset>
  <div class="vx-field"><label class="vx-label" for="demo-invalid">Code with an error (static example)</label><input class="vx-input is-invalid" id="demo-invalid" value="ABC" aria-invalid="true" aria-describedby="code-error"><span class="vx-error" id="code-error">Use a six-digit code.</span></div>
  <div class="vx-field"><label class="vx-label" for="demo-readonly">Read-only identifier</label><input class="vx-input" id="demo-readonly" value="VX-001" readonly></div>
  <div class="vx-field"><label class="vx-label" for="demo-disabled">Unavailable field</label><input class="vx-input" id="demo-disabled" disabled value="No access"></div>
  <button class="vx-btn" type="submit">Validate form</button>
</form>
Class / attributeUsage
vx-field / vx-label / vx-help / vx-errorGroup, label, help and error.
vx-input / vx-textarea / vx-selectNative controls.
vx-check / vx-checkbox / vx-radio / vx-switchSelection and toggling.
:user-invalid / .is-invalid / .vx-validated :invalidError after interaction, explicit state or form validation.
aria-invalid / aria-describedbyAssociate the error with the field and update the state in your application.

Tables

Use caption, scoped headers and a focusable wrapper when horizontal scrolling is available.

JavaScript: no

Team members
NameProfileStatus
AnaDesignActive
BrunoEngineeringPending
2 members
<div class="vx-table-responsive" role="region" aria-label="Team" tabindex="0">
  <table class="vx-table vx-table-striped vx-table-bordered vx-table-hover vx-table-compact">
    <caption>Team members</caption><thead><tr><th scope="col">Name</th><th scope="col">Profile</th><th scope="col">Status</th></tr></thead>
    <tbody><tr><th scope="row">Ana</th><td>Design</td><td><span class="vx-badge vx-badge-success">Active</span></td></tr><tr><th scope="row">Bruno</th><td>Engineering</td><td><span class="vx-badge vx-badge-warning">Pending</span></td></tr></tbody>
    <tfoot><tr><td colspan="3">2 members</td></tr></tfoot>
  </table>
</div>
Class / attributeUsage
vx-tableBase table.
vx-table-striped|bordered|hover|compactComposable variants.
vx-table-responsiveHorizontal scrolling; tabindex=0 enables keyboard access.

Breadcrumb and pagination

Real links represent destinations. The current page uses aria-current="page"; unavailable items have no href.

JavaScript: no

<nav aria-label="Breadcrumb navigation"><ol class="vx-breadcrumb"><li><a href="#inicio">Home</a></li><li><a href="#cards">Projects</a></li><li aria-current="page">VEXARO UI</li></ol></nav>
<nav aria-label="Example pages"><ol class="vx-pagination"><li><span aria-disabled="true">Previous</span></li><li><a href="#contextual" aria-current="page">1</a></li><li><a href="#tabelas">2</a></li><li><a href="#tabelas">Next</a></li></ol></nav>
Class / attributeUsage
vx-breadcrumb / --vx-breadcrumb-separatorBreadcrumb; default separator "/".
vx-paginationSemantic pagination.
aria-current="page" / aria-disabled="true"Current page and unavailable item.
vx-tabs / vx-tab-item / vx-tab-link.is-activeVisual-only tabs; use links for destinations and the JS version for panels.

Toast

Dynamic notifications with a dismiss button. The timer pauses on hover or focus. Essential messages must remain available in the page content.

JavaScript: yes

<button class="vx-btn" type="button" data-vx-toggle="toast" data-vx-message="Project saved successfully.">Show notification</button>
Class / attributeUsage
data-vx-toggle="toast" / data-vx-messageCreates a text message with a default five-second timer.
data-vx-toast / data-vx-targetAllows using an existing notification.
data-vx-dismiss="toast"Manual dismissal.
role="status" / role="alert"Routine / urgent announcement.

API

const toast = Vexaro.Toast.show('Changes saved.', { duration: 5000 });
// duration: 0 keeps the message until dismissed; urgent: true uses alert.
toast.close();
// Events: vx:toast:open, vx:toast:close

Tabs

Only one tab participates in the Tab order. Arrow keys switch tabs; Home and End go to the first and last. Each tab has its own panel.

JavaScript: yes

Ready to get started.
<div class="vx-w-full">
  <div class="vx-tabs" role="tablist" aria-label="Project details" data-vx-tabs id="demo-tabs">
    <button class="vx-tab-link" role="tab" type="button" id="tab-overview" aria-controls="panel-overview" aria-selected="true">Overview</button>
    <button class="vx-tab-link" role="tab" type="button" id="tab-history" aria-controls="panel-history" aria-selected="false" tabindex="-1">History</button>
  </div>
  <div class="vx-tab-panel" role="tabpanel" id="panel-overview" aria-labelledby="tab-overview" tabindex="0">Ready to get started.</div>
  <div class="vx-tab-panel" role="tabpanel" id="panel-history" aria-labelledby="tab-history" tabindex="0" hidden>Project created today.</div>
</div>
Class / attributeUsage
data-vx-tabs / role="tablist"Tab group.
vx-tab-link / role="tab" / aria-controlsButton and associated panel.
vx-tab-panel / role="tabpanel" / aria-labelledbyPanel and title.
aria-orientation="vertical"Uses the up and down arrows.

API

const tabs = Vexaro.getInstance('#demo-tabs');
tabs.select(1); // tab index or element
// Event: vx:tabs:change; detail.tab

Collapse and Accordion

Expansion animates height through CSS Grid. Collapsed content is inert. Use single for one open item; multiple allows several.

JavaScript: yes

No. Use native HTML, CSS and JavaScript.

Yes. Override the tokens in your CSS.

<div class="vx-accordion vx-w-full" data-vx-accordion="single">
  <h3 class="vx-accordion-heading"><button class="vx-btn vx-btn-secondary" type="button" data-vx-toggle="collapse" data-vx-target="#answer-one" aria-expanded="false" aria-controls="answer-one">Do I need a framework?</button></h3>
  <div class="vx-collapse" data-vx-collapse id="answer-one"><div class="vx-collapse-inner"><p class="vx-p-4">No. Use native HTML, CSS and JavaScript.</p></div></div>
  <h3 class="vx-accordion-heading"><button class="vx-btn vx-btn-secondary" type="button" data-vx-toggle="collapse" data-vx-target="#answer-two" aria-expanded="false" aria-controls="answer-two">Can I customize it?</button></h3>
  <div class="vx-collapse" data-vx-collapse id="answer-two"><div class="vx-collapse-inner"><p class="vx-p-4">Yes. Override the tokens in your CSS.</p></div></div>
</div>
Class / attributeUsage
data-vx-collapse / vx-collapse / vx-collapse-innerPanel and required inner child.
data-vx-toggle="collapse" / data-vx-targetTrigger.
data-vx-accordion="single|multiple"Single or multiple mode.
is-openInitially open state.

API

const collapse = Vexaro.getInstance('#answer-one');
collapse.open(); collapse.close(); collapse.toggle();
// Events: vx:collapse:open, vx:collapse:close

Tooltip

Extra information available on focus and hover. Escape dismisses it. You can hover the tooltip; essential content must remain visible on the page.

JavaScript: yes

<span class="vx-tooltip-wrap"><button class="vx-btn vx-btn-secondary" type="button" data-vx-toggle="tooltip" data-vx-target="#demo-tip">Token help</button><span class="vx-tooltip" id="demo-tip" role="tooltip" hidden>Tokens are shared CSS variables.</span></span>
Class / attributeUsage
vx-tooltip-wrap / vx-tooltipPositioning and content.
data-vx-toggle="tooltip" / data-vx-targetTrigger.
aria-describedbyPreserves existing descriptions and adds the tooltip.

API

const tip = Vexaro.getInstance('[data-vx-toggle="tooltip"]');
tip.open(); tip.close();
// Events: vx:tooltip:open, vx:tooltip:close

Popover

Extra content opened by clicking. Escape and outside clicks close it. Focus follows the natural order; the popover is not modal.

JavaScript: yes

<button class="vx-btn vx-btn-secondary" type="button" data-vx-toggle="popover" data-vx-target="#demo-popover" aria-controls="demo-popover" aria-expanded="false">About this version</button>
<div class="vx-popover" id="demo-popover" role="region" aria-labelledby="popover-title" hidden><h3 id="popover-title">VEXARO UI 1.0</h3><p>CSS-first, native components and documented public API.</p></div>
Class / attributeUsage
vx-popoverFixed panel repositioned in the viewport.
data-vx-toggle="popover" / data-vx-targetTrigger.
aria-controls / aria-expandedAssociation and state.

API

const popover = Vexaro.getInstance('[data-vx-toggle="popover"]');
popover.open(); popover.close(); popover.toggle();
// Events: vx:popover:open, vx:popover:close

Datepicker

Native date selection. The browser provides localization, calendar navigation and keyboard interaction. The public value uses YYYY-MM-DD; min, max and required follow HTML5.

JavaScript: yes

Choose a date between 2026 and 2030.
<div class="vx-field vx-w-full"><label class="vx-label" for="demo-date">Deadline</label><input class="vx-input" type="date" id="demo-date" data-vx-datepicker min="2026-01-01" max="2030-12-31" aria-describedby="date-help"><span class="vx-help" id="date-help">Choose a date between 2026 and 2030.</span></div>
Class / attributeUsage
input[type="date"] / vx-input / data-vx-datepickerNative calendar with optional API.
min / max / requiredNative constraints.
valueISO format; out-of-range values throw RangeError in the API.

API

const date = Vexaro.getInstance('#demo-date');
date.value = '2026-12-01';
// date.open() must be called during a user action.
// Event: vx:datepicker:change; detail.value

Autocomplete / Combobox

Text-filtered list that keeps focus in the input. Arrow keys navigate, Enter selects and Escape closes. Free text is accepted; validate required options in your application.

JavaScript: yes

Type to filter and use arrow keys to select.
<div class="vx-combobox vx-w-full" data-vx-combobox id="demo-combobox">
  <label class="vx-label" for="demo-city">City</label><input class="vx-input" id="demo-city" aria-describedby="city-help">
  <span class="vx-help" id="city-help">Type to filter and use arrow keys to select.</span>
  <ul class="vx-combobox-list" role="listbox" aria-label="Cities" id="city-options" hidden><li role="option">São Paulo</li><li role="option">Rio de Janeiro</li><li role="option">Belo Horizonte</li><li role="option">Curitiba</li></ul>
</div>
Class / attributeUsage
data-vx-combobox / vx-comboboxContainer.
input / [role="listbox"] / [role="option"]Field and text options.
aria-expanded / aria-activedescendant / aria-selectedManaged by the component.

API

const combo = Vexaro.getInstance('#demo-combobox');
combo.open(); combo.close();
// Event: vx:combobox:change; detail.value
// In a manual instance: new Vexaro.Combobox(element, { options: ['A', 'B'] })

Slider

Native range with a synchronized visible value. Arrow keys, Home and End are provided by the browser.

JavaScript: yes

<div class="vx-field vx-w-full"><label class="vx-label" for="demo-range">Volume: <output id="range-value" for="demo-range">40</output>%</label><input class="vx-range" type="range" id="demo-range" min="0" max="100" step="5" value="40" data-vx-slider data-vx-output="#range-value"></div>
Class / attributeUsage
vx-range / input[type="range"] / data-vx-sliderNative control.
data-vx-outputAssociated output selector.
min / max / stepLimits and increment.

API

const slider = Vexaro.getInstance('#demo-range');
slider.value = 75;
// Event: vx:slider:change; detail.value (number)

Table with sorting and pagination

Local sorting of loaded rows. Pagination shows a range of rows and announces the current page; it makes no server requests.

JavaScript: yes

Projects — click headers to sort
VEXARO18
Atlas7
North12
Center3
South9
<div class="vx-w-full">
  <div class="vx-table-responsive" role="region" aria-label="Sortable projects" tabindex="0"><table class="vx-table vx-table-striped" id="demo-data-table" data-vx-table data-vx-page-size="2" data-vx-pagination="#table-pages">
    <caption>Projects — click headers to sort</caption><thead><tr><th scope="col"><button type="button" data-vx-sort="text">Project</button></th><th scope="col"><button type="button" data-vx-sort="number">Tasks</button></th></tr></thead>
    <tbody><tr><th scope="row">VEXARO</th><td>18</td></tr><tr><th scope="row">Atlas</th><td>7</td></tr><tr><th scope="row">North</th><td>12</td></tr><tr><th scope="row">Center</th><td>3</td></tr><tr><th scope="row">South</th><td>9</td></tr></tbody>
  </table></div>
  <nav class="vx-flex vx-flex-wrap vx-items-center vx-gap-3 vx-mt-4" id="table-pages" aria-label="Project pages"><button type="button" class="vx-btn vx-btn-secondary vx-btn-sm" data-vx-page="prev">Previous</button><span data-vx-page-status role="status"></span><button type="button" class="vx-btn vx-btn-secondary vx-btn-sm" data-vx-page="next">Next</button></nav>
</div>
Class / attributeUsage
data-vx-table / data-vx-page-size / data-vx-paginationTable, page size and controls.
data-vx-sort="text|number"Sort button inside th.
data-vx-valueOptional cell value for sorting formatted numbers.
data-vx-page="prev|next" / data-vx-page-statusPagination controls and announcement.

API

const table = Vexaro.getInstance('#demo-data-table');
table.sort(1, 'descending', 'number');
table.goTo(2);
// Events: vx:table:sort (column, direction), vx:table:page (page)

Utilities

Utilities are optional and loaded after components. The available scale is 0, 1, 2, 3, 4, 5, 6, 8, 10, 12 and 16, referencing --vx-space-*.

Class / attributeUsage
vx-d-none|block|inline|inline-block|flex|gridDisplay; !important overrides the component display.
vx-m-* / vx-p-* / vx-gap-*Margin, padding and gap.
vx-mx-* / vx-my-* / vx-px-* / vx-py-*Inline and block axes.
vx-mt-* / vx-mb-* / vx-ms-* / vx-me-* / vx-pt-* / vx-pb-*Sides; ms/me follow the writing direction.
vx-m-auto / vx-mx-auto / vx-ms-autoAutomatic margins.
vx-d-md-flex / vx-p-lg-8 / vx-gap-sm-4Responsive: the same sm/md/lg/xl/xxl set.
vx-flex-md-row / vx-flex-lg-colResponsive direction.
vx-text-start|center|end / vx-text-muted / vx-w-fullText and width.
vx-visible / vx-invisible / vx-sr-onlyVisibility and screen-reader-only content.
<div class="vx-d-none vx-d-md-flex vx-gap-4 vx-p-4 vx-p-lg-8">
  Visible from 768px; larger padding from 992px.
</div>

JavaScript and lifecycle

The ESM entry exports classes, version and lifecycle functions. The global entry exposes the same members on Vexaro. Automatic initialization runs once when the page loads; call init again for content added later.

import { init, destroy, getInstance, Modal, Toast, setTheme } from 'https://ui.vexaro.solutions/dist/vexaro.js';
const area = document.querySelector('#minha-area');
init(area); // idempotent: does not duplicate listeners or instances
const modal = getInstance('#meu-modal');
modal.open();
modal.close();
Toast.show('Saved.', { duration: 5000 });
setTheme('dark');
destroy(area); // removes listeners, timers and references before unmounting
// When mounting again: init(area)
// Manual instances require an explicit instance.destroy().
Class / attributeUsage
init(scope = document)Returns new instances; includes scope itself when it matches.
getInstance(element|selector)Returns the auto-initialized instance or null.
destroy(scope = document)Destroys registered instances within the scope.
instance.destroy()Removes an instance's listeners and resources; do not reuse it after destroy.
setTheme("light"|"dark", element?)Updates the theme and emits vx:theme:change.
vx:init:errorMarkup error: detail.error and console logging.
vx:* eventsCustomEvent with bubbles=true. Component-specific data in detail.

Each component's API is in its own section. Only Modal should trap focus while open. The library does not inject message HTML: toast and combobox text uses textContent.

Integrations and stable API

The public API is frozen at version 1.0.0 according to API.md. Documented classes, tokens and methods may only be removed or change meaning in a major release. Compatible additions use minor; fixes use patch.

In React, Vue and Svelte, import CSS once and initialize components after mounting the DOM. Call destroy before unmounting. Use getInstance for auto-initialized components; do not create a second manual instance on the same element. See framework examples.

import { init, destroy } from 'https://ui.vexaro.solutions/dist/vexaro.js';
// After mounting:
init(container);
// Before unmounting:
destroy(container);

Optional Web Component, without Shadow DOM

import { registerVexaroElements } from './integrations/web-components.js';
registerVexaroElements();
<vx-components>
  <button class="vx-btn" type="button" data-vx-toggle="toast"
    data-vx-message="Integration ready.">Test</button>
</vx-components>

The adapter observes connection and disconnection. For content inserted later inside the element, call element.refresh(). It is not imported by the core and requires no framework or Shadow DOM.

Accessibility and quality

Use a consistent heading order, visible labels and associated descriptions. Tab and Shift+Tab navigate controls; Enter and Space activate buttons. Arrows control tabs, dropdowns, comboboxes and ranges. Escape closes modal, dropdown, tooltip and popover.

The 38 token contrast pairs are checked: text and semantic colors at least 4.5:1; borders and focus over provided backgrounds at least 3:1. Disabled states have reduced opacity. Custom colors, images and arbitrary combinations need their own assessment.

Reduced motion disables animations. Modal uses a native dialog and returns focus when closed; collapsed panels are inert. Datepicker and range use native browser semantics and keyboard behavior. The library does not automatically certify an application's WCAG compliance.

node scripts/build.mjs
npm test
node scripts/browser-check.mjs
# After installing development dependencies:
npm run test:browser
npm run test:visual:update

The local browser test uses headless Chrome and CDP; Playwright covers Chromium and Firefox, screenshots and axe-core. Human review of screen readers, zoom and final content is still required.

Open example dashboard

Changelog / version

1.0.0 · October 26, 2026

  • Complete CSS foundation: reset/reboot, grid, Flexbox, tokens and responsive utilities.
  • Forms, tables, navigation and visual components with square corners.
  • Modal, toast, dropdown, tabs, collapse, tooltip and popover with public APIs and events.
  • Native datepicker and slider, filterable combobox, sortable and paginated table.
  • Catalog with search, copyable demos, dashboard and ESM/global distribution.
  • Stable API contract and optional integration without Shadow DOM.

See CHANGELOG.md for the history and the board for review status.

VEXARO UI · Built with the library's own components.

Documentation