8000 GitHub - makanakidev/naki_ui: Naki UI is an open-source, Material-inspired component library and design system purpose-built for Jaspr — bringing Flutter-like declarative UI paradigms to Dart on the modern web with full SSR support and type-safe CSS-in-Dart. · GitHub
[go: up one dir, main page]

Skip to content
makanakidevPublic

About

Naki UI is an open-source, Material-inspired component library and design system purpose-built for Jaspr — bringing Flutter-like declarative UI paradigms to Dart on the modern web with full SSR support and type-safe CSS-in-Dart.

Topics

Resources

Stars

7 stars

Watchers

1 watching

Forks

Repository files navigation

Naki UI

Naki UI is a Material-inspired library of 80+ components for building fast, responsive web apps with Jaspr. If you know Flutter, you already know most of it: Scaffold, Column, ListView, showDialog and friends work the way you expect, rendered as plain HTML and CSS.

Naki UI Logo

Documentation  •  Live Demo

Features

  • 🧱 Flutter-style API: Compose Scaffold, Column, Row, Stack, ListView, Card and 80+ more components with the names and arguments you already know.
  • 💬 Overlays without boilerplate: showDialog, showModalBottomSheet and showSnackbar return results like Flutter's, with Flutter-matched transitions.
  • 🎨 Theming: Light, dark and system modes, color seeds, design tokens as CSS variables, and a smooth theme switch.
  • 🪟 Glass surfaces: Frosted and liquid glass navigation bars, glass cards and dialogs, with fallbacks for reduced transparency and higher contrast.
  • 📱 Mobile-first: Every component respects safe areas and Safari's collapsing toolbars, and responsive builders work with pure CSS.
  • ⚡ Server and static rendering: Purely visual components render on the server with zero JavaScript; interactive subtrees hydrate under @client.

Installation

dependencies:
  jaspr: ^0.23.5
  naki_ui: ^1.0.7
dart pub get

Naki UI has three entry points:

Import What it gives you
package:naki_ui/naki_ui.dart Components and the show* overlay functions
package:naki_ui/theme.dart Theme data, TextStyle, EdgeInsets, Gradient and styling
package:naki_ui/framework.dart Controllers, gestures, enums, PlatformData and lifecycle mixins

Quick start

Wrap your app in NakiApp (or NakiApp.router for multiple routes). It sets up the theme, document metadata and the overlay layer that showDialog and friends render into.

import 'package:jaspr/dom.dart'; // Color
import 'package:jaspr/jaspr.dart';
import 'package:naki_ui/framework.dart';
import 'package:naki_ui/naki_ui.dart';
import 'package:naki_ui/theme.dart';

@client
class App extends StatelessComponent {
  const App({super.key});

  @override
  Component build(BuildContext context) {
    return NakiApp(
      themeMode: ThemeMode.system,
      lightTheme: const LightThemeData(
        colorSeed: ColorSeed(primary: Color('#0f766e')),
      ),
      seo: const SEO(title: 'My App'),
      home: Scaffold(
        appBar: const AppBar(titleText: 'Naki UI'),
        body: Column(
          spacing: 16,
          children: [
            const Heading('Welcome'),
            Button.text('Get started', onTap: () => print('Tapped')),
          ],
        ),
      ),
    );
  }
}

With routing, pass routes from jaspr_router instead of home:

NakiApp.router(
  routes: [
    Route(path: '/', builder: (context, state) => const HomePage()),
    Route(path: '/settings', builder: (context, state) => const SettingsPage()),
  ],
)

Important

In Jaspr static or server mode, mark the top of each interactive subtree with @client. Event handlers, overlays, controllers and theme switching only work inside one. Arguments that cross the boundary must be serializable, so don't pass functions or components into a @client component.


Dialogs, bottom sheets and snackbars

Show an overlay from any BuildContext below a NakiApp or Scaffold. No controller, no state, and the future completes with the value it closed with:

// A dialog that returns the button the user picked.
final delete = await showDialog<bool>(
  context: context,
  builder: (context) => Dialog(
    title: 'Delete item?',
    barrierDismissible: true,
    actions: [
      Button.text('Cancel', onTap: () => closeDialog(context, false)),
      Button.text('Delete', onTap: () => closeDialog(context, true)),
    ],
  ),
);

// A modal bottom sheet: the builder returns its content.
final target = await showModalBottomSheet<String>(
  context: context,
  builder: (context) => Column(
    children: [
      Button.text('Messages', onTap: () => closeModalBottomSheet(context, 'messages')),
      Button.text('Mail', onTap: () => closeModalBottomSheet(context, 'mail')),
    ],
  ),
);

// A snackbar. Showing another replaces it; closeSnackbar() closes it early.
showSnackbar(context, const Snackbar(content: NakiText('Saved')));

Both futures complete with null when the barrier or Escape dismisses the overlay. Pass the builder's context to closeDialog and closeModalBottomSheet, not the page's.

For an overlay that lives in your component tree, pass an OverlayController instead:

final controller = OverlayController();

Column(
  children: [
    Button.text('Open', onTap: controller.open),
    Dialog(controller: controller, title: 'Hello'),
  ],
)

Drawer, Popover and Tooltip complete the overlay set. Popovers and tooltips flip and slide to stay on screen and clear of the app bars.


Theming

Pick colors with a seed, then switch modes from anywhere:

NakiApp(
  themeMode: ThemeMode.system,
  cacheThemeMode: true, // remembers the user's choice
  lightTheme: const LightThemeData(colorSeed: ColorSeed(primary: Color('#0f766e'))),
  darkTheme: const DarkThemeData(colorSeed: ColorSeed(primary: Color('#14b8a6'))),
  home: const HomePage(),
)

// Anywhere below it:
Button.text('Toggle theme', onTap: context.toggleTheme);
context.setTheme(ThemeMode.dark);
final text = context.textColor; // typed shortcuts for theme tokens

Wrap a subtree in NakiThemeProvider to give it a different theme.

Glass

Card.glass(child: NakiText('Frosted card'));

Dialog(controller: controller, glass: true, title: 'Glass dialog');

BottomNavigationBar(
  type: BottomNavigationBarType.liquidGlass, // or frostedGlass
  items: const [
    BottomNavigationBarItem(icon: LucideIcons.icon_house, label: 'Home'),
    BottomNavigationBarItem(icon: LucideIcons.icon_search, label: 'Search'),
  ],
)

Tune them with the glassTint, glassRim, glassBlur and bottomNavbarGlass* theme properties. Liquid glass bends the content behind it in Chromium and falls back to frosted glass elsewhere.


Layout and scrolling

Column and Row take a spacing argument, so you rarely need spacers:

Column(
  spacing: 16,
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [Heading('Title'), NakiText('Body')],
)

ListView.builder and GridView.builder render large collections in batches:

ListView.builder(
  itemCount: 1000,
  initialItemCount: 24,
  loadMoreItemCount: 24,
  itemBuilder: (context, index) => NakiText('Item $index'),
)

Scaffold places its AppBar, BottomNavigationBar, Drawer and Sidebar inside the device's safe areas, and keeps them there while Safari's toolbar collapses.


Forms and inputs

TextField, FormBuilder, AutoCompleteField, SegmentedInput (PINs and one-time codes), Checkbox, RadioButton, Switch, Slider, Dropdown and Calendar, all with validation built in. Calendar picks dates, times or ranges:

Calendar(
  id: 'trip-dates',
  type: CalendarType.range,
  rangeValidator: (days) => days == null ? 'Select your dates' : null,
  onSelectedRange: (days) => print('${days.first} to ${days.last}'),
)

Responsive layouts

ResponsiveBuilder swaps layouts with CSS media queries, so it renders on the server with no layout shift:

ResponsiveBuilder(
  mobile: const MobileNav(),
  tablet: const TabletNav(),
  desktop: const DesktopNav(),
)

Use BreakPointWrapper(minWidth: 640, maxWidth: 1024, 764D child: ...) for custom ranges, MediaQueryProvider to measure an element, and PlatformData() for the OS, browser and form factor.


Agent skills

Naki UI ships skills that teach coding agents its API. Activate the CLI and install them for your agent:

dart pub global activate naki_ui
naki_ui skills --claude-code   # or --cursor, --antigravity, --copilot, ...

It installs naki-ui-fundamentals, naki-ui-framework and naki-ui-theming, plus the companion Jaspr skills (skip them with --no-jaspr).

Supported agents and CLI options
Agent CLI Flag Option (--agent) Destination Directory
Antigravity --antigravity antigravity .agents/skills
Cursor --cursor cursor .cursor/skills
Claude Code --claude-code, --claude claude-code .claude/skills
Cline --cline cline .cline/skills
Codex --codex codex .agents/skills
GitHub Copilot --copilot copilot .github/skills
Command Code --command-code command-code .commandcode/skills
OpenCode --opencode opencode .opencode/skills
Continue --continue continue .continue/skills
Windsurf --windsurf windsurf .windsurf/skills
General --general, --generic general .agents/skills
  • --list / -l: List the available skills.
  • --skill <name>: Install or remove one skill.
  • --target <path>: Install to a custom directory.
  • --all: Install for every agent detected in the workspace.
  • --force / -f: Overwrite skills even if unchanged.
  • --dry-run: Preview the changes.
  • --clean / --remove: Remove installed Naki UI skills.
  • --[no-]jaspr: Install the companion Jaspr skills (on by default).

Learn more

License

MIT. See LICENSE.

About

Naki UI is an open-source, Material-inspired component library and design system purpose-built for Jaspr — bringing Flutter-like declarative UI paradigms to Dart on the modern web with full SSR support and type-safe CSS-in-Dart.

Topics

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

0