@espressif/dashboard-ui-components
v1.1.1
Published
Reusable React UI components, page layouts, and utilities — Espressif's dashboard design system.
Readme
@espressif/dashboard-ui-components
Reusable React UI components, page layouts, and utilities — Espressif's dashboard design system.
Built with React, TypeScript, Tailwind CSS v4, and Radix UI primitives.
Table of Contents
- Features
- Tech Stack
- Installation
- Setup
- Theming
- Usage
- Bundle size, Babel warnings, and imports
- AI agent rules (Cursor & Claude Code)
- Available Components
- Updating the Library
- Project Structure
- Development
- Versioning
- Contributing
Features
- Reusable, composable React components
- Layout shells for dashboards and entry pages
- TypeScript support with exported types
- Tailwind CSS v4 design tokens and shared preset
- ESM sub-path exports (
/components,/layouts,/common,/utils) so apps can narrow the entry graph; fine-grained per-component bundle shrinking is limited by the pre-built chunks (see Bundle size, Babel warnings, and imports) - Pure components — no internal state management, routing, or API calls
- Published to public npm (semver ranges, pre-built ESM, no consumer-side build)
Tech Stack
- React 18 / 19
- TypeScript
- Tailwind CSS v4
- Radix UI primitives
- Framer Motion
- Vite (tsup bundler)
Installation
This library is published to public npm under the @espressif scope. No registry configuration or authentication is required — consumers download a pre-built tarball at install time (no clone, no consumer-side build).
npm install @espressif/dashboard-ui-componentsRequires Node.js 20.19+. Any package manager works — the package ships prebuilt ESM, so there is no consumer-side build step.
Setup
1. Import Styles
Import the library's design tokens and base styles in your global Tailwind CSS entry. This one line is all a Tailwind v4 consumer needs — the imported file already tells Tailwind (via @source) to scan the library's compiled dist/ for utility classes, so you do not need to add a second @source in your app or list the package in a content array.
/* app/globals.css (or your Tailwind entry CSS) */
@import "@espressif/dashboard-ui-components/styles";2. Peer Dependencies
Required:
npm install react react-dom tailwindcssOptional (only if you use specific components):
npm install @tanstack/react-table # DataTable component
npm install react-hook-form # Form component
npm install shiki # Code component
npm install recharts # Chart componentsOptional: JS-config projects (tailwind.config.ts)
Tailwind v4 is CSS-first, so a tailwind.config.ts is not required. If your app still ships one — for example a project migrated from v3, or a framework that generates it — pull in the shared preset so token utilities (bg-primary, text-sidebar-foreground, …) are recognised:
// tailwind.config.ts
import espPreset from '@espressif/dashboard-ui-components/tailwind-preset'
export default {
presets: [espPreset],
content: [
'./src/**/*.{ts,tsx}',
'./node_modules/@espressif/dashboard-ui-components/dist/**/*.{js,mjs}',
],
}Theming
The library exposes every design token as a CSS custom property. Consumer apps retheme by redeclaring those variables in their own CSS after the @import. No fork, no Tailwind config change, no build step.
Overriding an existing token (e.g. sidebar colour)
Colours are defined in two scopes: :root for light mode and .dark for dark mode. To restyle a token consistently, override in both scopes — a :root-only override will be shadowed by the library's .dark block when the app switches to dark mode.
/* app/globals.css */
@import "@espressif/dashboard-ui-components/styles";
:root {
--color-sidebar: #10b981;
--color-sidebar-foreground: #ffffff;
--color-sidebar-accent: #059669;
--color-sidebar-accent-foreground: #ffffff;
--color-sidebar-primary: #047857;
--color-sidebar-primary-foreground: #ffffff;
--color-sidebar-ring: #10b981;
}
.dark {
--color-sidebar: #064e3b;
--color-sidebar-foreground: #d1fae5;
--color-sidebar-accent: #065f46;
--color-sidebar-accent-foreground: #ffffff;
/* …repeat for every sidebar-* token you want changed in dark mode */
}Every component that uses bg-sidebar, text-sidebar-foreground, etc. now picks up the new palette automatically — no component-level props needed.
Adding a brand-new token
Use Tailwind v4's @theme block to declare a new token. Tailwind will generate the matching utilities (bg-brand, text-brand, border-brand, …):
@import "@espressif/dashboard-ui-components/styles";
@theme {
--color-brand: #ff7a00;
--color-brand-foreground: #ffffff;
}<button className="bg-brand text-brand-foreground">Ship it</button>What can be themed
All tokens live in the library's src/styles/colors.css and src/styles/globals.css. Grouped for reference:
Brand colours — --color-primary + -light / -dark / -foreground, --color-secondary + -foreground / -container / -container-foreground, --color-tertiary (same shape as secondary)
Status colours — --color-error, --color-warning, --color-success, --color-info (each with -light / -dark / -foreground), plus --color-destructive + -foreground, --color-disabled + -foreground
Surface / neutral colours — --color-background, --color-foreground (+ -secondary, -muted, -disabled), --color-card, --color-popover, --color-muted, --color-accent (each with -foreground), --color-surface (+ -foreground, -variant, -variant-foreground, -container-lowest, -container-low, -container, -container-high, -container-highest), --color-border, --color-input, --color-ring
Sidebar — --color-sidebar, -foreground, -primary, -primary-foreground, -accent, -accent-foreground, -border, -ring
Header — --color-header, --color-header-border
Charts — --color-chart-1 through --color-chart-10, plus --color-chart-success, -error, -warning, -disabled, -gray, -primary, -secondary
Layout dimensions (light and dark share the same value; override in :root):
--espd-sidebar-width, --espd-sidebar-width-collapsed, --espd-sidebar-width-mobile, --espd-header-height, --espd-banner-height, --espd-footer-height, --espd-hidden-footer-height, --espd-sidebar-item-font-size, --espd-sidebar-flyout-group-label-font-size
Typography and radius — --font-sans (whole font stack; override in @theme to replace), --radius-lg (drives -md and -sm)
Tips
- Put your overrides after the
@importline; CSS cascade order is what makes the override win. - Prefer redeclaring existing tokens (in
:root/.dark) over adding new ones — the built-in tokens are what every component reads from. - If you only need light mode, a single
:rootblock is enough. If you support both, always mirror the change in.dark. - Overriding
--font-sansrequires an@themeblock so Tailwind picks it up:@theme { --font-sans: "Inter", system-ui, sans-serif; }
Customising the app logo
WorkspaceLayout and EntryLayout shipped originally with a required appName: AppName prop pointing at Espressif-internal presets (espressif, rainmaker, insights, …). External consumers should omit appName and use one of the two consumer paths below instead. The internal preset path is still available for Espressif apps.
Option 1 — Pass any React node (simplest)
Every logo slot on the layouts accepts an optional ReactNode. Render whatever you like — an <img>, an <svg>, a Next.js <Image>, a component from your own design system. No wrapper, no aspect-ratio math.
import { WorkspaceLayout } from '@espressif/dashboard-ui-components/layouts'
<WorkspaceLayout
logo={<img src="/logo.svg" alt="Acme" style={{ height: 32 }} />}
logoCollapsed={<img src="/logo-mark.svg" alt="" style={{ height: 24 }} />}
logoMobile={<img src="/logo-mark.svg" alt="" style={{ height: 20 }} />}
footerBranding={<a href="https://acme.com">© Acme, Inc.</a>}
sidebarConfig={/* … */}
>
{/* … */}
</WorkspaceLayout>logoCollapsed falls back to logo; logoMobile falls back to logo. Pass footerBranding={false} to hide the sidebar-footer branding entirely.
EntryLayout accepts the single logo prop:
<EntryLayout logo={<img src="/logo.svg" alt="Acme" />} heading="Welcome">
{/* … */}
</EntryLayout>Option 2 — Reuse the built-in AppLogo container with your own assets
If you want the library's animated container (light/dark switching, collapse animation, sidebar-width bounds) but with your own images, pass a LogoAssetConfig object to logoAssets. Layouts feed it into <AppLogo> for you.
import type { LogoAssetConfig } from '@espressif/dashboard-ui-components/common'
const acmeLogoAssets: LogoAssetConfig = {
full: {
light: { src: '/assets/logo/acme.png', width: 198, height: 28 },
dark: { src: '/assets/logo/acme-dm.png', width: 198, height: 28 },
},
minimal: {
light: { src: '/assets/logo/acme-mark.svg', width: 26, height: 32 },
dark: { src: '/assets/logo/acme-mark.svg', width: 39, height: 32 },
},
}
<WorkspaceLayout
logoAssets={acmeLogoAssets}
footerBranding={<a href="https://acme.com">© Acme, Inc.</a>}
sidebarConfig={/* … */}
>
{/* … */}
</WorkspaceLayout>The shape is { full: { light, dark? }, minimal?: { light, dark? } }. Optional fields fall back sensibly:
darkomitted → useslightfor both modes.minimalomitted → the sidebar collapsed state and mobile top-bar reusefull.- Simplest case (one asset works everywhere):
{ full: { light: { src: '/logo.svg', width: 180, height: 36 } } }
Each asset can also carry a per-asset alt; otherwise the component-level alt is used.
Overflow behaviour. For logoAssets (and the AppLogo component in general), the sidebar and mobile-header slots enforce hard CSS caps: the rendered <img> is clamped by the wrapping container's max-width, max-height, and object-fit: contain. Numeric values in your asset config are treated as intrinsic-size hints — even wildly oversized width / height values shrink to fit the sidebar rail (~224px expanded, ~40px collapsed) and the header height. You don't have to trim your source images to the pixel.
For Tier 1 (logo / logoCollapsed / logoMobile / footerBranding — a raw React node) the layout renders your JSX verbatim without a bounding wrapper, so you own the sizing. Set max-height, object-fit, or a fixed size on your own node if you're rendering large source images.
You can also use <AppLogo> directly if you're rendering it outside the layout:
import { AppLogo } from '@espressif/dashboard-ui-components/common'
<AppLogo logo={acmeLogoAssets} />
<AppLogo logo={acmeLogoAssets} minimal darkMode width={120} />width, when passed, overrides the asset's width and scales height proportionally.
Option 3 — Internal Espressif preset (Espressif apps only)
Espressif apps continue to use the shorthand:
<WorkspaceLayout appName="rainmaker" sidebarConfig={/* … */}>
{/* … */}
</WorkspaceLayout>Setting appName also enables the default Espressif footer link. External consumers should not use this path; AppName is a closed union of Espressif-internal product ids.
Sidebar footer branding
WorkspaceLayout's sidebar footer branding is controlled by footerBranding:
| Value | Result |
|-------|--------|
| undefined (default) | Shows the Espressif logo + link when appName is an internal preset. Renders nothing when appName is omitted. |
| false | Hides the footer branding entirely. |
| A React node | Renders your node verbatim (no opacity wrapper — style it however you like). |
The older sidebarConfig.hideCompanyBranding: true still works as a shorthand for footerBranding={false}. Prefer footerBranding in new code.
Usage
Prefer sub-path entry points over the package root unless you need components, common modules, and layouts from a single entry:
// Recommended: import only the surface you need (narrower module graph than the root barrel)
import { Button, Card, Alert } from '@espressif/dashboard-ui-components/components'
import { WorkspaceLayout } from '@espressif/dashboard-ui-components/layouts'
import { AppLogo } from '@espressif/dashboard-ui-components/common'
import { cn } from '@espressif/dashboard-ui-components/utils'// Root entry — re-exports components, common, and layouts together.
// Use when you intentionally depend on multiple surfaces; otherwise prefer sub-paths above.
import { Button, Card, Alert } from '@espressif/dashboard-ui-components'Use named imports for the symbols you use. Avoid import * as DashboardUI from '…' for this package (worse tree-shaking hints and noisier bundles).
Types can be imported alongside values:
import {
DateRangePicker,
type DateRangePickerPresetId,
} from '@espressif/dashboard-ui-components/components'Bundle size, Babel warnings, and imports
Why you may see [BABEL] … deoptimised the styling … exceeds the max of 500KB
This library is shipped as pre-bundled ESM (built with tsup). Published files include large shared chunks (for example dist/chunk-*.js). When your toolchain runs Babel on those files (common with Metro or other setups that transform node_modules), Babel switches to compact code generation for very large single files and logs an informational message. It does not mean the build is invalid; it means Babel is skipping expensive “pretty” output for that file.
What makes the shipped JS large
- Coarse chunks: The
/componentsentry re-exports the full public component surface. Unused named exports can often be dropped by your bundler, but anything statically reachable from what you import stays (shared chunk code, assets pulled in by those modules). - Eager assets: Some components ship inlined data (for example Lottie JSON for
AnimatedCard, raw SVG forIllustration, and image assets embedded at build time). Importing those components pulls that payload into your graph. - Dependencies: Radix UI, Framer Motion, Lottie, cmdk, lucide-react, date-fns, etc. are bundled into the library build (see
package.json). Heavy peers such asrechartsandshikiare not bundled; install them only when you use chart or syntax-highlighted code components. The markdown stack (react-markdown,remark-gfm,rehype-slug) is likewise not bundled, but it is a regular dependency — it installs with the library and needs no action from you, and bundlers drop it from your app build whenMarkdownContentis unused.
How to keep consumer builds sensible
- Use
@espressif/dashboard-ui-components/components,/layouts,/common, or/utilsinstead of the root package when you do not need everything together. - Install optional peer dependencies only for features you use (
@tanstack/react-table,react-hook-form,shiki,recharts). - Prefer production builds when measuring size; dev servers and Babel-on-vendor paths often look worse than optimized output.
- For web apps, use a bundle analyzer on your app if you need to chase kilobytes; expect a sizable vendor chunk for a full-featured UI kit.
Not supported for app code: importing from @espressif/dashboard-ui-components/core or deep paths that are not listed under package.json exports. Stick to the documented entry points.
AI agent rules (Cursor & Claude Code)
This package ships the same consumption guidance — supported import surfaces, optional peers, styling, and bundle expectations — for two AI coding agents:
| Agent | Shipped file |
|-------|--------------|
| Cursor | .cursor/rules/dashboard-ui-consumption.mdc |
| Claude Code | .claude/rules/dashboard-ui-consumption.md |
Cursor
Cursor reads rules from each repo's own .cursor/rules/ directory; there is no extends mechanism for .mdc files. Copy the rule from node_modules into your repo:
mkdir -p .cursor/rules
cp node_modules/@espressif/dashboard-ui-components/.cursor/rules/dashboard-ui-consumption.mdc \
.cursor/rules/Claude Code
Claude Code loads project memory from CLAUDE.md and any files it @-imports. Reference the shipped guide directly from your CLAUDE.md so it stays in sync with the installed version:
@./node_modules/@espressif/dashboard-ui-components/.claude/rules/dashboard-ui-consumption.mdOr copy it into your repo if you prefer to commit a local copy:
mkdir -p .claude/rules
cp node_modules/@espressif/dashboard-ui-components/.claude/rules/dashboard-ui-consumption.md \
.claude/rules/Keep both current after upgrades
{
"scripts": {
"sync-agent-rules": "mkdir -p .cursor/rules .claude/rules && cp -f node_modules/@espressif/dashboard-ui-components/.cursor/rules/*.mdc .cursor/rules/ && cp -f node_modules/@espressif/dashboard-ui-components/.claude/rules/*.md .claude/rules/"
}
}Re-run after upgrading the library to pick up guidance changes. Commit the copied files so your team and CI agents see the same rules.
Available Components
Components (/components)
| Component | Description |
|-----------|-------------|
| Accordion | Animated expandable content sections |
| Avatar | User avatar with image and fallback |
| List / ListGroup / ListItem | Structured list display; rows are buttons by default, or real links when an item sets href and List is given a linkComponent adapter. Styling hooks: className / itemClassName / separatorClassName / ListGroup.labelClassName / ListItem.className, plus the always-present classes in LIST_CLASS_NAMES (.espd-list, .espd-list-group-label, .espd-list-item, .espd-list-custom-item, .espd-list-separator) |
| Sidebar | Collapsible sidebar navigation |
| Menu | Context and action menus |
| Tooltip | Hover tooltips |
| Sheet | Slide-out panel |
| Dropdown Menu | Dropdown action menu |
| ScrollArea | Custom scrollable container |
| Form / Label / Checkbox | Form controls |
| Button / ButtonGroup | Standard and grouped buttons |
| Card | Content card container |
| Input / InputPassword / Textarea | Text input fields |
| AsyncCombobox | Async searchable combobox |
| SearchBox / AdvancedSearchBox | Search inputs |
| Breadcrumb | Navigation breadcrumbs |
| Separator | Visual divider |
| Skeleton / TableSkeleton | Loading placeholders |
| Table / DataTable | Data tables with sorting and pagination |
| Tabs | Tabbed content panels |
| Dialog / ConfirmationDialog / FullscreenDialog | Modal dialogs |
| Pagination | Page navigation controls |
| Badge | Status and label badges |
| Alert | Alert banners (optionally dismissible) |
| Link | Styled anchor element |
| Code | Syntax-highlighted code blocks |
| MarkdownContent | Renders a markdown string with library primitives (GFM, heading anchors; raw HTML is escaped, not parsed) |
| CopiableText / CopyButton | Copy-to-clipboard utilities |
| ProgressBar | Progress indicator |
| Collapsible / CollapsibleCard | Expandable content sections |
| SimpleCard / BasicDetailsCard / NoDataCard | Specialized card variants |
| PreviewCard | Content preview card |
| PageContainer | Page wrapper with header |
| DynamicList | Dynamic item list |
| RequirementList | Met/unmet checklist (password rules, prerequisites); check for satisfied rows, muted circle for outstanding ones, with screen-reader state labels |
| TogglableItemsList | Toggleable item list |
| GlitchText / GradientText / HighlightText / ShimmeringText | Text effects |
| LottieAnimationContainer | Lottie animation wrapper |
| FullSizeError / InlineError | Error display components |
Layouts (/layouts)
| Component | Description | |-----------|-------------| | WorkspaceLayout | Full dashboard shell with sidebar, header, and footer | | EntryLayout | Authentication and entry page layout |
Common (/common)
| Component | Description | |-----------|-------------| | AppLogo | Application logo | | ProfileCard | User profile display card | | CopyrightCard | Copyright notice | | PageLoader | Full-page loading indicator | | MyAccountMenu | Account menu component | | FooterCard | Footer content card |
Updating the Library
How you upgrade depends on whether the new version fits inside your existing semver range.
Patch updates within the current range
If package.json already pins "^0.1.0" and the registry has a newer patch like 0.1.3, pull it in with:
npm update @espressif/dashboard-ui-componentspackage.json is not modified; package-lock.json is updated to the new patch. Commit the lockfile change.
Plain
npm install(no args) does not pick up newer patches — it respects the existing lockfile. Usenpm update(ornpm install @espressif/dashboard-ui-componentswith the package name) to re-resolve.
Minor or major bumps (range needs to widen)
When the registry has a new minor or major version outside your current range, edit the range explicitly:
npm install @espressif/dashboard-ui-components@^0.2.0Both package.json and package-lock.json change. Commit both.
Check the installed version
npm ls @espressif/dashboard-ui-componentsSee available versions
Browse the package's versions on npm for the full list of published releases.
Project Structure
src
├── assets # Static assets (images, icons)
├── common # Shared components (AppLogo, ProfileCard, etc.)
├── components # Composed UI components
├── core # Core primitives, hooks, and utilities
├── layouts # Page layout shells
├── styles # CSS design tokens and global styles
├── types # Shared TypeScript types
├── utils # Utility functions
└── index.ts # Main barrel exportDevelopment
Prerequisites
- Node.js
24.18.1— pinned in.nvmrcand.node-version - npm
11.16.0— the version bundled with that Node
If you use nvm, the repo includes an .nvmrc file:
nvm install
nvm usenpm ci and every npm run script hard-fail with EBADDEVENGINES on a different
toolchain — the requirement is declared in devEngines in package.json. This is a
development-only requirement; consumers of the published package only need
Node 20.19+ (see Installation).
The source repository is maintained internally by Espressif. The sections below are for maintainers working in that repository.
Getting Started
Install dependencies:
npm installRun the demo app:
npm run dev:demoThe demo resolves this package from src/ via Vite aliases. To iterate on the built output (dist/) the same way consumers do, run a watch build in another terminal:
npm run dev:libDeveloping alongside a consumer app (local package)
Consumer apps usually live in separate repositories. They depend on published artifacts: package.json exports point at dist/, not TypeScript in src/. Now that this library is published to public npm, consumers install it with a semver range (for example npm install @espressif/dashboard-ui-components@^0.1.0) with no registry configuration. Keeping dev:lib running while the consumer uses file: or a packed tarball is the closest day-to-day analogue to that install layout. So when you link the library locally, the consumer still loads whatever is in dist/. Deleting the consumer’s package-lock.json and reinstalling does not rebuild dist/; only npm run build or npm run dev:lib in this repo does.
A prepublishOnly script runs tsup when npm publish is invoked (i.e., in CI before the package is uploaded to the registry). It does not run on npm install or npm ci — local or CI — so contributor installs are fast and consumer installs from the registry never trigger a build.
Typical workflow (matches a future npm install):
- In the library repo:
npm run dev:lib(rebuildsdist/on every change). - In the consumer repo:
npm run dev(or your app’s dev server).
Point the consumer at your local checkout using either:
file:in the consumer’spackage.json:"@espressif/dashboard-ui-components": "file:../path/to/esp-ui-component-library"With npm 10, local
file:dependencies are often installed as a symlink; confirm withreadlink node_modules/@espressif/dashboard-ui-components(macOS/Linux) or your OS equivalent.npm link: in the library repo runnpm link, then in the consumer runnpm link @espressif/dashboard-ui-components.
Vite consumers: pre-bundling can cache stale copies of the package. If changes do not show up, exclude the package from dependency optimization (and clear the cache once if needed):
// vite.config.ts
export default defineConfig({
optimizeDeps: {
exclude: ['@espressif/dashboard-ui-components'],
},
// …
})If you use server.watch.ignored, ensure the path to the linked library is not ignored.
Duplicate React: npm link can pull in a second copy of react and break hooks/context. Fix with npm overrides, align versions, or follow npm link patterns so the app and the library share one React instance.
Optional — fastest HMR (dev only): in the consumer, resolve.alias can map @espressif/dashboard-ui-components (and any subpaths you import, e.g. /layouts) to this repo’s src/ entry files. This library’s source uses the @/ alias to src/; mirror the same mapping as in demo/vite.config.ts (@ → library src) so internal imports resolve. Validate before release against dist/ (or a packed tarball), because production consumers use the built package, not raw src/.
Optional — publish-like install (separate clones): run npm pack in this repo, then in the consumer npm install /absolute/or/relative/path/to/espressif-dashboard-ui-components-0.x.x.tgz. That exercises the same file layout as the published tarball without going through the registry. Alternatively, install directly from public npm — npm install @espressif/dashboard-ui-components@<version> — which needs no .npmrc in the consumer. yalc is another option for pushing local packages into a consumer without a monorepo.
When to run npm install in the consumer again: when this library’s version or dependency list changes, or when switching between git/tag installs and file:. You should not need to delete package-lock.json for routine UI edits if watch + linking work.
Troubleshooting: if nothing updates — check dist/ timestamps, that dev:lib is running, Vite’s node_modules/.vite cache, and duplicate React. If the browser throws does not provide an export named 'useSyncExternalStore' from .../use-sync-external-store/shim/...: the shim is CommonJS; Vite must pre-bundle it, and resolve.preserveSymlinks: true often breaks dedupe with file: deps (resolution stays under the library’s nested node_modules). Fix:
- Turn off
preserveSymlinks(omit it or setfalse). - Keep
resolve.dedupe:['react', 'react-dom', 'use-sync-external-store']. - In the consumer run
npm install use-sync-external-storeso a copy exists at the app root. - Add
optimizeDeps.include:['use-sync-external-store', 'use-sync-external-store/shim']alongside your existingexcludefor@espressif/dashboard-ui-components. - Delete
node_modules/.viteand restart dev.
resolve: {
dedupe: ['react', 'react-dom', 'use-sync-external-store'],
// do not set preserveSymlinks: true for local file: / linked UI libraries
},
optimizeDeps: {
exclude: ['@espressif/dashboard-ui-components'],
include: ['use-sync-external-store', 'use-sync-external-store/shim'],
},Monorepos: npm/pnpm workspaces are optional if an app ever lives in the same repo; they are not required for the flows above.
Deploying the Demo
To deploy the demo to AWS (S3 + CloudFront):
- In
demo/, copy.env.exampleto.envand setS3_BUCKETandCLOUDFRONT_ID. - Ensure AWS credentials are configured (default profile).
- From the repo root:
npm run deploy:demoThis builds the demo, syncs demo/dist to the S3 bucket, and creates a CloudFront invalidation. The live demo is served from https://dqsptdnp5qxr0.cloudfront.net.
Build the library:
npm run buildDeveloping Using Cursor
This project includes Cursor AI skills that automate common scaffolding and audit tasks. Open the project in Cursor and use the example prompts below to trigger each skill.
| Skill | Description | How to Use |
|-------|-------------|------------|
| Create Component | Scaffold a new UI component in src/components/ with all required files, barrel exports, and demo entry | "Create a new component called StatusIndicator" |
| Create Layout | Scaffold a new page layout in src/layouts/ with all required files, barrel exports, and demo entry | "Create a new layout called DashboardLayout" |
| Create Common Module | Scaffold a new common module in src/common/ with all required files, barrel exports, and demo entry | "Create a new common module called ThemeSwitcher" |
| Add Demo | Add a demo entry for an existing component or common module that is missing one | "Add a demo for the Badge component" |
| Audit Exports | Audit the library for missing exports, missing demos, and wiring inconsistencies | "Audit component exports and demo coverage" |
Versioning
This project follows Semantic Versioning.
MAJOR.MINOR.PATCH
| Version | Meaning | |---------|---------| | 0.1.0 | Initial release | | 0.2.0 | New components or layouts added | | 0.2.1 | Bug fix | | 1.0.0 | First stable release / breaking changes |
Contributing
- Create a feature branch from
release/staging - Make your changes (new components, layouts, bug fixes, etc.)
- Follow existing component patterns and file structure
- Add TypeScript types for all props
- Keep components pure — no internal API calls, routing, or state management
- Raise a merge request to merge your feature branch into
release/staging - Get the MR reviewed and approved
Example branch name:
feature/add-date-pickerRelease Process
The release and publishing flow is maintained internally by Espressif and is
documented in RELEASING.md (not included in the published npm
package). In short: pushing a vX.Y.Z tag triggers CI, which builds, runs
typecheck + tests on Node 24.18, and publishes the same tarball to public npm and the
internal GitLab registry.
License
Licensed under the Apache License 2.0.
© 2026 Espressif Systems (Shanghai) CO LTD
Third-party software & trademarks
This package depends on third-party open-source software (all under permissive
licenses — MIT, Apache-2.0, ISC). Those dependencies are installed separately via
npm and remain under their own licenses; see THIRD-PARTY-NOTICES.md
for the list, and NOTICE for attribution and trademark information.
"Espressif", the Espressif logo, and Espressif product logos are trademarks of Espressif Systems (Shanghai) CO LTD. The Apache-2.0 license does not grant any trademark rights (see Section 6 of the License).
