even-toolkit

Design system & component library for Even Realities G2 smart glasses apps.

55+ web components, 191 pixel-art icons, glasses SDK bridge with per-screen architecture, pixel-accurate G2 text measurement, speech-to-text module, light/dark themes, and design tokens — all following the Even Realities 2025 UIUX Design Guidelines.

Live Demo → even-demo.vercel.app

Install

npm install even-toolkit

Scaffold a new app instantly:

npx @even-toolkit/create-even-app my-app
# or
npx even-toolkit my-app

Choose from 6 templates: minimal, dashboard, notes, chat, tracker, media.

What's Inside

/web — Web Component Library

55+ React components with Tailwind CSS, designed for mobile-first companion apps.

import { Button, Card, NavBar, ListItem, Toggle, AppShell } from 'even-toolkit/web';

Primitives: Button, Card, Badge, Input, Textarea, Select, MultiSelect, Checkbox, RadioGroup, Slider, InputGroup, Skeleton, Progress, StatusDot, Pill, Toggle, SegmentedControl, Table, Kbd, Divider

Layout: AppShell, Page, NavBar, NavHeader, SideDrawer, DrawerShell, DrawerTrigger, ScreenHeader, SectionHeader, SettingsGroup, CategoryFilter, ListItem (swipe-to-delete), SearchBar, Tag, TagCarousel, TagCard, PagedCarousel, CardCarousel, SliderIndicator, PageIndicator, StepIndicator, Timeline, StatGrid, StatusProgress

Feedback: TimerRing, Dialog, ConfirmDialog, Toast, EmptyState, Loading, BottomSheet, CTAGroup, ScrollPicker, DatePicker, TimePicker, SelectionPicker

Charts (recharts): Sparkline, LineChart, BarChart, PieChart, StatCard

Media: ChatContainer, ChatInput, Calendar, FileUpload, VoiceInput, ImageGrid, ImageViewer, AudioPlayer

/web/icons — 191 Pixel-Art Icons

Official Even Realities icon set: 32x32 grid, 2x2px units, 6 categories.

import { IcChevronBack, IcTrash, IcSettings } from 'even-toolkit/web/icons/svg-icons';

<IcChevronBack width={20} height={20} />

Categories: Edit & Settings (32), Feature & Function (50), Guide System (20), Menu Bar (8), Navigate (23), Status (54), Health (12)

Glasses SDK

Everything needed to build G2 glasses apps with a clean, per-screen architecture.

Per-Screen Architecture (v1.4)

Each glasses screen lives in its own file with co-located display + action logic:

src/glass/
  shared.ts              — Snapshot type + actions interface
  selectors.ts           — Screen router (3 lines of wiring)
  splash.ts              — Splash image + loading text
  AppGlasses.tsx         — useGlasses hook setup
  screens/
    home.ts              — { display, action }
    detail.ts            — { display, action }
    active.ts            — { display, action }

Define a screen

import type { GlassScreen } from 'even-toolkit/glass-screen-router';
import { buildScrollableList } from 'even-toolkit/glass-display-builders';
import { moveHighlight } from 'even-toolkit/glass-nav';

export const homeScreen: GlassScreen<MySnapshot, MyActions> = {
  display(snapshot, nav) {
    return {
      lines: buildScrollableList({
        items: snapshot.items,
        highlightedIndex: nav.highlightedIndex,
        maxVisible: 5,
        formatter: (item) => item.title,
      }),
    };
  },

action(action, nav, snapshot, ctx) {
    if (action.type === 'HIGHLIGHT_MOVE') {
      return { ...nav, highlightedIndex: moveHighlight(nav.highlightedIndex, action.direction, snapshot.items.length - 1) };
    }
    if (action.type === 'SELECT_HIGHLIGHTED') {
      ctx.navigate(`/item/${snapshot.items[nav.highlightedIndex].id}`);
      return nav;
    }
    return nav;
  },
};

Wire screens together

import { createGlassScreenRouter } from 'even-toolkit/glass-screen-router';
import { homeScreen } from './screens/home';
import { detailScreen } from './screens/detail';

export const { toDisplayData, onGlassAction } = createGlassScreenRouter({
  'home': homeScreen,
  'detail': detailScreen,
}, 'home');

Navigation Helpers (glass-nav)

import { moveHighlight, clampIndex, calcMaxScroll, wrapIndex } from 'even-toolkit/glass-nav';

// Clamped movement (0 to max)
moveHighlight(current, 'up', max)    // Math.max(0, Math.min(max, current - 1))
moveHighlight(current, 'down', max)  // Math.max(0, Math.min(max, current + 1))

// Clamp index to button count
clampIndex(index, buttonCount)       // Math.min(Math.max(0, index), count - 1)

// Max scroll offset
calcMaxScroll(totalLines, slots)     // Math.max(0, totalLines - slots)

// Wrapping movement (loops around)
wrapIndex(current, 'down', count)    // (current + 1) % count

Display Builders (glass-display-builders)

import {
  buildScrollableList,
  buildScrollableContent,
  slidingWindowStart,
  G2_TEXT_LINES,          // 10
  DEFAULT_CONTENT_SLOTS,  // 7 (below glassHeader)
} from 'even-toolkit/glass-display-builders';

// Scrollable highlighted list with scroll indicators
const lines = buildScrollableList({
  items: recipes,
  highlightedIndex: nav.highlightedIndex,
  maxVisible: 5,
  formatter: (r) => r.title,
});

// Header + scrollable content with indicators
const display = buildScrollableContent({
  title: 'Recipe Detail',
  actionBar: buildStaticActionBar(['Start'], 0),
  contentLines: ['Line 1', 'Line 2', ...],
  scrollPos: nav.highlightedIndex,
});

Pixel-Accurate Text Measurement (pretext)

Use even-toolkit/pretext when character-count wrapping is not precise enough. It wraps @evenrealities/pretext and uses the same LVGL font metrics as Even Hub for G2 text width, truncation, and wrapped-height prediction.

import {
  getTextWidth,
  measureGlassText,
  truncateGlassText,
  G2_TEXT_MAX_WIDTH,
} from 'even-toolkit/pretext';

const title = truncateGlassText('A long glasses title', { width: G2_TEXT_MAX_WIDTH, paddingX: 12 });

const measured = measureGlassText('The quick brown fox jumps over the lazy dog', {
  width: 300,
  paddingX: 8,
  borderWidth: 2,
});

console.log(measured.lineCount, measured.height, measured.maxLineWidth);
console.log(getTextWidth(title));

Speech-to-Text (STT)

Provider-agnostic speech-to-text module for voice input in G2 glasses apps.

Quick Start

import { useSTT } from 'even-toolkit/stt/react';

function VoiceInput() {
  const { transcript, isListening, start, stop } = useSTT({
    provider: 'soniox',
    language: 'en-US',
    apiKey: 'your-soniox-key',
  });

return (
    <div>
      <button onClick={isListening ? stop : start}>
        {isListening ? 'Stop' : 'Record'}
      </button>
      <p>{transcript}</p>
    </div>
  );
}

Configuration

useSTT({
  provider: 'soniox',
  language: 'en-US',        // BCP-47 language tag
  apiKey: 'your-key',       // Required
  vad: { silenceMs: 2500 }, // Auto-stop after silence
  chunkIntervalMs: 4000,    // Progressive transcription interval
  continuous: false,         // Don't auto-stop on silence
})

Design Tokens

Light theme following Even Realities 2025 guidelines:

@import "even-toolkit/web/theme-light.css";
@import "even-toolkit/web/typography.css";
@import "even-toolkit/web/utilities.css";
Token Value Usage
--color-text #232323 Primary text (TC-1st)
--color-text-dim #7B7B7B Secondary text (TC-2nd)
--color-bg #EEEEEE Page background (BC-3rd)
--color-surface #FFFFFF Card/component background (BC-1st)
--color-accent #232323 Accent/highlight (BC-Highlight)
--color-positive #4BB956 Success/connected (TC-Green)
--color-negative #FF453A Error/warning (TC-Red)
--color-accent-warning #FEF991 Active/toast (BC-Accent)
--radius-default 6px Default border radius
--font-display FK Grotesk Neue Display & body font

Typography

Style Size Weight Tracking
Very Large Title 24px 400 -0.72px
Large Title 20px 400 -0.6px
Medium Title 17px 400 -0.17px
Medium Body 17px 300 -0.17px
Normal Title 15px 400 -0.15px
Normal Body 15px 300 -0.15px
Normal Subtitle 13px 400 -0.13px
Normal Detail 11px 400 -0.11px