Eduport Design System Components v0.7.0
Eduport Design System · v0.7.0 Web components, React wrappers and design tokens for Eduport products. Every component on this page is live: click it, type in it, tab through it, then flip the theme.
npm i @eduportdesign/tokens @eduportdesign/web-components See it in use

Use it in your project

These steps work for a new app or an existing one. Components are Lit web components, so they run in any framework or none. React apps get wrappers with typed props and events. Everything is on npm:

  • @eduportdesign/tokens: colors, type, spacing, radius, shadow and motion as CSS variables, SCSS and JS.
  • @eduportdesign/web-components: the components, as custom elements like <ep-button>.
  • @eduportdesign/react: React wrappers like <Button>.

1. Install the packages

# Plain HTML, Vue, Svelte, Angular or any other framework
npm install @eduportdesign/tokens @eduportdesign/web-components

# React (the wrappers bring the web components with them)
npm install @eduportdesign/tokens @eduportdesign/react

# The Onest and Gabarito fonts (recommended; without them text falls back to the system font)
npm install @fontsource-variable/onest @fontsource-variable/gabarito

2. Load the CSS once

Import these at your app's entry point (main.js, main.tsx or similar), before any component renders. Without tokens.css the components have no colors.

import '@fontsource-variable/onest';
import '@fontsource-variable/gabarito';
import '@eduportdesign/tokens/css';                 // required: light theme, Clay palette, soft corners
import '@eduportdesign/tokens/css/theme-dark';      // optional: dark theme
import '@eduportdesign/tokens/css/palette-sage';    // optional: Sage & Sand palette
import '@eduportdesign/tokens/css/corners-sharp';   // optional: sharp corners
import '@eduportdesign/tokens/css/base';            // recommended: body, headings, text, links, focus

The base stylesheet styles the page itself (background, text, headings, links and the focus ring) so everything outside the components matches them. It resets heading and paragraph margins, so space your layout with gap, and any rule in your own CSS overrides it.

3. Pick a theme, palette and corner style

Set data-theme (light or dark), data-palette (clay or sage) and data-corners (soft or sharp) on the root element. All are optional, default to light, Clay and soft, and can change at runtime. The Sage & Sand palette needs palette-sage loaded.

<html data-theme="dark" data-palette="sage" data-corners="sharp">

To follow the operating system's light or dark setting:

const dark = matchMedia('(prefers-color-scheme: dark)');
const apply = () => (document.documentElement.dataset.theme = dark.matches ? 'dark' : 'light');
apply();
dark.addEventListener('change', apply);

4. Use the components

Plain HTML or JavaScript (with Vite or any bundler)

// main.js
import '@eduportdesign/web-components';                     // every component
import '@eduportdesign/web-components/components/button';   // or only the ones you use

<!-- index.html -->
<ep-text-field label="Email" name="email" type="email" required></ep-text-field>
<ep-button variant="primary" type="submit">Sign up</ep-button>

Attributes are kebab-case (helper-text); the matching JS properties are camelCase (helperText). Events bubble, so read new values from event.target. Form controls work inside a native <form> with no extra wiring.

No build step

For a prototype or a static page, load everything from the jsDelivr CDN:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@eduportdesign/tokens@0.7.0/dist/css/tokens.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@eduportdesign/web-components@0.7.0/+esm"></script>

<ep-button variant="primary">Save</ep-button>

React

Component names drop the prefix and use PascalCase: ep-text-field becomes TextField.

import { useState } from 'react';
import { Button, TextField } from '@eduportdesign/react';

export function SignUp() {
  const [email, setEmail] = useState('');
  return (
    <form onSubmit={(e) => { e.preventDefault(); save(email); }}>
      <TextField label="Email" type="email" value={email} onInput={(e) => setEmail(e.target.value)} />
      <Button variant="primary" type="submit">Sign up</Button>
    </form>
  );
}

In Next.js, put 'use client' at the top of files that use the components.

Vue

Tell Vue that ep- tags are custom elements, then use them in templates as in plain HTML.

// vite.config.js
import vue from '@vitejs/plugin-vue';

export default {
  plugins: [vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith('ep-') } } })],
};

Angular

Add the CSS to styles in angular.json, import the components in main.ts, and allow custom elements in each component that uses them.

// angular.json → projects.<app>.architect.build.options
"styles": ["node_modules/@eduportdesign/tokens/dist/css/tokens.css", "src/styles.css"]

// main.ts
import '@eduportdesign/web-components';

// any.component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

@Component({ standalone: true, schemas: [CUSTOM_ELEMENTS_SCHEMA], template: '<ep-button>Save</ep-button>' })
export class AnyComponent {}

Svelte

No setup beyond steps 1 to 3. Import the components in main.js and use the tags directly.

5. Style your own UI with the tokens

Use the semantic --ep-* variables instead of hex values, so your screens switch theme and corner style along with the components.

.card {
  background: var(--ep-color-surface-raised);
  color: var(--ep-color-text-primary);
  border-radius: var(--ep-radius-container);
  padding: var(--ep-space-300);
}

Adding it to an existing app

  • Import only the components you need, one path at a time, to keep bundles small.
  • The theme attributes work on any element, so you can adopt it one screen at a time with <section data-theme="light">.
  • The tokens are namespaced --ep-* and the tags ep-*, so they won't clash with your existing styles.

Next steps

Browse every component below, or every prop, slot and event in Storybook. The README covers installing from GitHub Packages. If you use an AI coding assistant, point it at docs/llm-guide.md.

A dashboard, built from the system

Nothing here is custom: the rail, hero, cards, covers, rings, sparklines and avatars are the published components. Switch the theme or corners at the top and the whole screen follows.

Home Courses Timetable Assignments 4 Grades Settings
Thursday · Term 2, week 7 Physics 11 starts at 10:30. You have 16 submissions waiting.

Today

  • Physics 11 · OpticsLab 2
  • Mathematics 10 · QuadraticsRoom 14
  • Chemistry 12 · TitrationLab 1

Submissions this week

63

Example screen

A few components working together, the way they might in a learning app.

Course registration closes on Friday. Pick your electives before then.

Physics for Class 11

Enrolled Motion, forces and energy, with weekly practice tests and live doubt-clearing sessions.
24 lessons 18 hours 1,240 students
+38 classmates Syllabus Continue

Ask a doubt

Post anonymously
Clear Send question

Color

--ep-color-*

Two palettes for the Signal style: Clay (coral on warm clay, the default) and Sage & Sand. Each has one key color and its own neutrals, and both share six category colors that tell things apart without saying good or bad. Switch palettes at the top of the page. Components only read the semantic tokens, which change with the theme. Each swatch shows the text color that passes WCAG AA on it, and its contrast ratio.

Key color · Coral

Neutral · Clay

Category colors subtle · bold · text, per tone

Typography

--ep-font-*

Onest for everything you read, Gabarito for the big headings, JetBrains Mono for code. Use the role tokens (--ep-font-heading-lg, --ep-font-body-md) instead of picking a size and weight.

heading-xlGood morning, Anjali
heading-lgYour courses this term
heading-mdSubmissions waiting for review
heading-smPhysics 11 · Optics practical
body-lgMotion, forces and energy, with weekly practice tests.
body-mdMotion, forces and energy, with weekly practice tests and live doubt-clearing sessions.
label-mdAssignments due this week
code-mdconst tone = course.subject.tone;

Elevation

--ep-shadow-*

Surfaces rise in steps: the canvas, the sheet on it, raised cards on the sheet, then overlays that float above everything. In dark mode the surface color does most of the lifting, with a brighter edge instead of a bigger shadow.

insetInputs
controlSecondary buttons
raisedCards, stats
raised-hoverCards under the pointer
overlayMenus, dialogs, toasts

Spacing

--ep-space-*

Every margin, padding and gap is a step on this 4px-based scale. Small steps are for the inside of components; page layout uses multiples of 8px. Full rules in docs/layout.md.

Space scale
TokenValueSizeUse for
Proximity
Don't Everything 12px apart. Which label goes with which field?
Do 4px inside each field group, 24px between groups.

Layout grid

--ep-layout-*

Pages sit on a 4, 8 or 12 column grid. The columns, page margin, gutter and section gap change at each breakpoint by themselves, so a page written once adapts. Resize the window to watch this one change.

Breakpoints and layout tokens
BreakpointMin widthColumnsMarginGutterSection gapHeader
base0416px16px32px56px
md600px824px24px40px64px
lg1024px1232px24px48px64px
xl1440px1240px32px48px64px
Content widths
--ep-layout-width-form 560px · forms, settings
--ep-layout-width-prose 68ch · running text
--ep-layout-width-page 1280px · page content

Page structure

Most screens share one shell: a top bar, side navigation from lg (a drawer below it), then a page header and sections. The wireframe below responds to its own width with a container query, the way a reusable layout should.

Motion and timing

--ep-motion-* data-motion

Interactions are fast, quiet and predictable. Motion only explains a change: a state changing, something entering or leaving, where something moved, or that work is in progress. Nothing in a component runs longer than 300ms, only opacity and transform animate, and reduced motion keeps the fades but drops the movement. Turn on Reduce motion below to compare. Full policy in docs/motion.md.

This page
Reduce motion

Sets data-motion="reduced" on the page, the way an app's own setting would. Your operating system's reduced-motion setting applies too, and this can't turn it off.

Motion tokens
TokenDefaultReducedUse for
--ep-motion-duration-fast100ms100msColor, border and opacity changes; tooltips
--ep-motion-duration-normal200ms100msModals entering; anything leaving
--ep-motion-duration-slow300ms100msToasts entering; the most any component uses
--ep-motion-duration-slower400ms100msDrawers and page-level panels only
--ep-motion-duration-move200ms0msMovement inside a control: switch thumb, tab indicator
--ep-motion-distance-sm4px0Tooltip, menu and popover enter offset
--ep-motion-distance-md8px0Toast enter offset
--ep-motion-scale-enter0.981Modal enter scale
Tooltip delay

Hover waits 500ms. Once one is open, slide along the row and the next opens at once. Tab to them and they open with no wait.

Loading button
Quick save Slow upload

The quick one finishes in 250ms, so no spinner flashes. The slow one shows a spinner after 400ms. Extra presses while busy are ignored.

Toast timing
Short message Long message Delete with undo

Toasts stay 6 seconds, plus 1 second for every 20 words over 20. A toast with an action stays until you dismiss it. Hover or focus pauses the timer.

Enter and state
Email me when a student submits Open modal
Not allowed
  • Shake on error, ripple, press scale-down or hover lift
  • Bounce, spring or overshoot easing
  • Animated number counters, typewriter text, parallax, auto-playing carousels
  • Animated or pulsing focus rings
Students and parents will see the grades for Physics, Class 11 straight away. Cancel Publish grades

Button

<ep-button>

Starts an action. Use one primary button per view. Works with forms through type="submit" and type="reset".

Variants
Primary Secondary Tertiary Ghost Danger
Sizes
Small Medium Large
With icons
New course Download Next Delete
Disabled
Primary Secondary Tertiary Ghost
Loading
Save changes Uploading…
Full width
Sign in

Icon button

<ep-icon-button>

A square button with only an icon. The label is required and is what screen readers announce. Hover or focus for the tooltip.

Variants
Sizes

Text field

<ep-text-field>

A single-line input with a label, helper text and error message. Takes part in forms like a native input.

States
Prefix and suffix
Sizes

Textarea

<ep-textarea>

Multi-line text. Set maxlength to show a character count.

States

Select

<ep-select>

Picks one option from a list. Built on the native select, so it uses the phone's own picker and works with every screen reader.

States

Checkbox

<ep-checkbox>

Turns one option on or off, or picks several from a list. Supports an indeterminate state for "select all".

States
Unchecked Checked Indeterminate Disabled Checked and disabled
Select all
All subjects
Physics Chemistry Mathematics

Radio group

<ep-radio-group> <ep-radio>

Picks exactly one option. Tab moves into the group, arrow keys move between options.

Vertical Monthly Per term Yearly Lifetime (sold out)
Horizontal Easy Medium Hard

Switch

<ep-switch>

A setting that takes effect straight away.

States
Email notifications Show my profile to classmates Offline downloads Two-step verification

Date picker

<ep-date-picker> <ep-calendar>

Type a date in your locale's order or pick it from the calendar. The value is always YYYY-MM-DD. In the calendar, arrows move by day and week, and Page Up and Page Down change the month.

Default
Calendar alone

Badge

<ep-badge>

A short, non-interactive status or count. The text always says what the status is, so it never relies on color.

Variants
Draft New In review Passed Due soon Overdue
With dot or icon
Online Away Live at 5 PM Recommended
Counts
Notifications3 Messages12

Tag

<ep-tag>

A keyword, category or active filter. Removable tags have their own button, so they work by keyboard.

Variants
Physics Featured Video Free Beta Archived
Removable
Class 11 CBSE Physics Under 2 hours Locked filter

Avatar

<ep-avatar>

A person's picture, falling back to initials and then an icon. Always round, even with sharp corners.

Sizes
Fallbacks

Card

<ep-card>

Groups related content. Three styles: outlined, elevated and filled. Media, header actions and footer are optional.

Variants

Outlined

The default. A border separates it from the page. Open

Elevated

A shadow lifts it off the page. Use for the main item in a view.

Filled

A tinted background with no border. Good for quiet, secondary groups.

Cover art

<ep-cover>

Generated artwork for course tiles and empty states, in a category color. The same seed always draws the same picture, so give each course its own. It is decorative and hidden from screen readers.

Stat

<ep-stat>

One headline number with its label and an optional note or chart. Use a few in a row at the top of a dashboard.

+6 this week Oldest: 3 days

Sparkline

<ep-sparkline>

A trend with no axes, as a line, a line with an area, or bars. The last value is marked. Always give it a label.

+12 this week +3 points Steady

Progress ring

<ep-progress-ring>

A circular completion indicator with the percentage in the middle. Always determinate.

Tones and sizes

Avatar group

<ep-avatar-group>

Overlapping avatars that collapse into a count past max.

Max 3

Icon tile

<ep-icon-tile>

A rounded square holding an icon in a category tone: the identity mark for a subject, a team or a calendar.

Tones
Sizes

Table

<ep-table>

Styles a native <table> you write, so any cell content works. A button in a header plus aria-sort makes a sortable column; aria-selected on a row highlights it. It scrolls sideways on narrow screens.

Sortable
Class 11 students
Fees Actions
Small, striped
Timetable
PeriodMondayTuesdayWednesday
1PhysicsMathsChemistry
2MathsEnglishBiology
3ChemistryPhysicsMaths
4EnglishBiologyPhysics

Icon

<ep-icon>

Icons from Tabler (over 5,000 available). They size to the text and use its color. The components ship with the set below; apps can register any other Tabler icon.

Accordion

<ep-accordion> <ep-accordion-item>

Collapsible sections for long pages where people look for one part, like an FAQ. Up and Down arrows move between headers.

Default Open the course, choose Live classes and select Join. The button turns on five minutes before the start. Yes, in the mobile app. Downloads stay available for 30 days. Each correct answer earns the marks shown next to it. Wrong answers on entrance tests lose a quarter mark.
Single June to September: units 1 to 4. October to December: units 5 to 8. January to March: revision and final exams.

Empty state

<ep-empty-state>

What to show when a list or page has nothing in it yet. Say why it's empty and offer the next step.

Page Assignments your teachers post will show up here. Browse courses
Small, no results Check the spelling or clear the filters. Clear filters

Skeleton

<ep-skeleton>

Placeholders in the shape of content that is loading, so the page doesn't jump. Mark the loading region with aria-busy="true".

Card

App shell

<ep-app-shell> <ep-top-bar> <ep-side-nav> <ep-page-header>

The frame every screen sits in: a sticky top bar, the side navigation and the content area, spaced as the page structure describes. From 1024px the navigation sits beside the content; below that it moves into a drawer that the top bar's menu button opens. The shell adds the skip link, the <main> landmark, the page margin and the content width cap. See it full screen in Storybook.

Top bar Eduport Teach
Side nav
Home Courses Timetable Assignments 4 Grades Settings
Page header Home Courses Everything you teach this term. Import New course
<ep-app-shell>
  <ep-top-bar slot="top-bar">
    <a slot="brand" href="/">Eduport Teach</a>
    <ep-icon-button slot="actions" icon="bell" label="Notifications"></ep-icon-button>
  </ep-top-bar>
  <ep-side-nav slot="nav">
    <ep-side-nav-item href="/" icon="home">Home</ep-side-nav-item>
    <ep-side-nav-item href="/courses" icon="book" current>Courses</ep-side-nav-item>
  </ep-side-nav>

  <ep-page-header heading="Courses">
    Everything you teach this term.
    <ep-button slot="actions">New course</ep-button>
  </ep-page-header>
  <!-- sections -->
</ep-app-shell>

Tabs

<ep-tabs> <ep-tab> <ep-tab-panel>

Switches between views in the same place. Arrow keys move between tabs, Home and End jump to the ends.

Default Overview Lessons Tests Certificate An introduction to motion, forces and energy. 24 lessons, about 18 hours in total. Lesson 1: Units and measurement. Lesson 2: Motion in a straight line. Lesson 3: Motion in a plane. Three practice tests and one final test. You can retake practice tests as often as you like. Finish every lesson to unlock your certificate.

Pagination

<ep-pagination>

Moves between pages of a long list. Listen for change and load the page in page. In narrow containers the numbers collapse to "Page 4 of 12".

Default
Narrow

Alert

<ep-alert>

An inline message about a page or section. Each variant has its own icon, so meaning never depends on color alone.

Variants
The portal will be unavailable on Sunday from 2 to 4 AM. Your teacher will review it by Thursday. Pay before 10 October to avoid a late fee. Pay now Remind me later The file is larger than 25 MB. Compress it and try again. An alert can also be a single line with no heading.

Toast

toast() <ep-toast>

A brief notification in the corner of the screen. Screen readers announce it, and its timer pauses while you hover or focus it.

Try it
Success Info Warning Error
Static
Your profile changes are saved. Lesson moved to Archive. Undo

Progress bar

<ep-progress-bar>

How far a task has got. Leave out value when the amount of work isn't known.

Determinate
Indeterminate

Spinner

<ep-spinner>

A short wait of unknown length. Set label="" when nearby text already says what's happening.

Sizes
In a button Saving…

Tooltip

<ep-tooltip>

A short label on hover or focus. Escape closes it, and you can move the pointer onto it without it disappearing.

Placements
Top Bottom Left Right

Popover

<ep-popover>

Interactive content anchored to a button, like a filter form. It doesn't block the page; clicking outside or pressing Escape closes it.

Filters Filters
Only students with pending fees Apply

Logos

@eduportdesign/logos

Every Eduport logo as an SVG file. Start with the orange wordmark on light backgrounds and the white one on orange or dark backgrounds. Keep clear space of half the wordmark's height around it, don't go below 80 px wide (24 px for the symbol), and never recolor, stretch or add effects. Each file can be downloaded.