Skip to content

Repository files navigation

Plass UI

Plass UI

license npm latest package npm downloads pub package

Live previews and full props for every component, in both frameworks. This README is the map; each package has a quick start of its own.

Plass UI components arranged around the Plass mark: a search field, a settings card, radio buttons and checkboxes, buttons and chips, a segmented button and pagination, a success alert, a slider, a progress bar and a meter, an avatar stack, a badge and a rating


Plass is one design language, shipped as two libraries. 127 components made of tinted glass and gradients, in React and in Flutter, under the same names and the same numbers. React adds three more for problems only the DOM has.

Every surface answers one question: is this pressed, or does it hold something? A thing you press is a tinted pane, filled with a gradient that turns through its colour family and lit by a bloom that follows your pointer. A thing that holds something is clear glass: translucent, deeply blurred, never dyed. Nothing is bevelled or embossed, and depth is carried by light, colour and blur.

Highlights

  • Finished when you install it. The gradients, shadows, blur, focus ring and press response are already decided and already agree with each other. There is no theme file to fill in.
  • Learn it once, use it on both. An md control is 40px in either framework, primary is the same colour family, and one documentation page covers the two.
  • Five props, not fifty. size, color, variant, density and elevation mean the same thing on every component, so the tenth one costs nothing to learn after the first.
  • Readable because it was measured. Every gradient stop clears 4.5:1 against its own label, the lightest corner included.
  • Accessible without the checklist. Roles, labels, keyboard operation and focus management live inside the components.
  • Dark mode you do not write. It follows the platform and can be forced either way on any subtree.
  • Nothing you did not ask for. The npm package is ESM and tree-shakeable, so only what you import is bundled. The pub package has no dependencies at all.

Packages

Package Registry Requires Quick start
packages/react npm: plass-ui React 18 or 19, Node.js 20.19 or later README
packages/flutter pub.dev: plass_ui Flutter 3.41 or newer (Dart 3.11) README

The two version independently and keep separate changelogs. packages/react/CHANGELOG.md is the React package's, packages/flutter/CHANGELOG.md the Flutter one's. A release on one side is not a release on the other, so the numbers will not always agree.

Install

React

npm install plass-ui
/* your app's CSS entry point */
@import "plass-ui/styles.css";
import { PlButton, PlTextField } from 'plass-ui';

<PlTextField label="Email" type="email" fullWidth />
<PlButton type="submit">Sign in</PlButton>
<PlButton variant="glass" color="secondary">Cancel</PlButton>

react and react-dom are peer dependencies, React 18 or 19. The stylesheet is finished CSS: Tailwind CSS v4 builds this package and does not have to be installed in yours, though there is a second entry point for a project that already runs it. Every component has an entry point of its own (plass-ui/button) and nearly all of them carry 'use client', so a Next.js Server Component can import one with nothing configured, and PlTable, whose columns are render callbacks, deliberately does not, so a server-rendered page can use its own API.

The React quick start has the rest.

Flutter

flutter pub add plass_ui
import 'package:flutter/widgets.dart';
import 'package:plass_ui/plass_ui.dart';

PlButton(
  onPressed: save,
  child: const Text('Save'),
)

Nothing else to install: no dependencies, no assets, no platform channels, no stylesheet and no provider. It is built on package:flutter/widgets.dart alone, so it drops into any Flutter app without bringing a second design system in behind it, and it is unaffected by material.dart and cupertino.dart moving out of the framework into material_ui and cupertino_ui.

The Flutter quick start has the rest.

The page under the components

This is the one setup step neither package can do for you, and skipping it is the fastest way to conclude the glass is broken. Plass draws controls and sheets; it does not paint your background, but a sheet of glass over a flat white page has nothing to be in front of, and every translucent surface will read as opaque. Two tokens exist for exactly this, and they are the same two on both sides:

body {
  background: linear-gradient(160deg, var(--plass-bg-from) 0%, var(--plass-bg-to) 100%);
  background-attachment: fixed;
  color: var(--plass-fg);
}
final tokens = PlassTheme.of(context);

DecoratedBox(
  decoration: BoxDecoration(
    gradient: LinearGradient(
      begin: Alignment.topCenter,
      end: Alignment.bottomCenter,
      colors: <Color>[tokens.bgFrom, tokens.bgTo],
    ),
  ),
  child: ...,
)

Any backdrop with structure in it works. What does not work is nothing at all.

Documentation

Page What you will find
Getting started Install and setup, end to end, in either framework.
Examples Whole screens built out of the components, a dashboard, a landing page, a sign-up flow.
All components Every component, one page each: live previews and the full props table.
Design language Why a Plass surface looks and behaves the way it does.
Prop conventions The shared vocabulary every component draws from.
Colour The token families, the measured contrast, and how to theme them.
Changelog What changed in each release.

Components

Every component is exported under a Pl prefix. Button, Card and Table are the most-taken identifiers in the ecosystem, and a consumer should not have to alias ours on import.

The list below is both packages'. The props are the same props under Dart's names, and each component's page says exactly what differs.

A † marks a name that is React-only. PlFlex, PlPortal and PlVisuallyHidden are the three components React adds. They are not omissions. Each one answers a problem only the DOM has, and the Dart answer is a line of framework code rather than a component. Their pages say which.

The counts at the top of this page are one component per folder. The list also names two exports that are not counted, because each shares a folder and a page with another component: PlToggleGroup, beside PlToggle, and PlContextMenu, a second trigger onto PlMenu's surface. PlContextMenu is the fourth †, for a reason of its own: no right-click gesture means the same thing on every platform Flutter runs on, so a Flutter app opens a PlMenu from onLongPress itself.

Charts

PlAreaChart · PlBarChart · PlGaugeChart · PlHeatmapChart · PlLineChart · PlPieChart · PlScatterChart · PlSparkline · PlTimelineChart

Display

PlAppLogo · PlAvatar · PlBadge · PlBlockquote · PlBreadcrumb · PlChip · PlCodeBlock · PlDataList · PlDataTable · PlDivider · PlGallery · PlHighlight · PlHotKeys · PlIcon · PlImage · PlList · PlMockup · PlStat · PlTable · PlTextLink · PlTimeline · PlTree · PlTypography · PlVisuallyHidden†

Feedback

PlAlert · PlConfirmProvider · PlDrawer · PlEmpty · PlMeter · PlModal · PlOverlay · PlPopconfirm · PlPopover · PlProgressBox · PlProgressCircular · PlProgressLinear · PlSkeleton · PlToast · PlTooltip · PlTour

Inputs

PlButton · PlButtonGroup · PlCalendar · PlCheckbox · PlColorPicker · PlCombobox · PlDatePicker · PlDateRangePicker · PlDateTimePicker · PlFieldset · PlFilePicker · PlFloatingActionButton · PlForm · PlIconButton · PlNumberField · PlOtpField · PlPagination · PlRadioGroup · PlRating · PlSegmentedButton · PlSelect · PlSlider · PlSwitch · PlTextField · PlTimePicker · PlToggle · PlToggleGroup · PlTransfer · PlTreeSelect

Layout

PlAspectRatio · PlContainer · PlFlex† · PlFooter · PlGrid · PlHeader · PlPageLayout · PlPanes · PlPortal† · PlScrollArea · PlScrollZone · PlShow · PlSidebar · PlStack

Navigation

PlAnchor · PlBackTop · PlBottomNavigation · PlCommandPalette · PlContextMenu† · PlFloatingBottomNavigation · PlMenu · PlMenubar · PlNavigationMenu · PlStepper

Surfaces

PlAccordion · PlBox · PlCard · PlCarousel · PlChatBubble · PlCollapsible · PlHoverCard · PlHowToSteps · PlPill · PlSpoiler · PlTabs · PlToolbar · PlWindowPane

Transitions

PlAnimateAppear · PlAnimateBlink · PlAnimateCounter · PlAnimateFade · PlAnimateFloat · PlAnimateGrow · PlAnimateHeadline · PlAnimateLighting · PlAnimateMarquee · PlAnimateReveal · PlAnimateRotate · PlAnimateScramble · PlAnimateShake · PlAnimateSlide · PlAnimateSplit · PlAnimateTyping · PlAnimateZoom

Setting defaults

size, color, density and the date vocabulary can be decided once for an application rather than at every call site. Optional. The library is finished without it.

import { PlassProvider } from "plass-ui";

<PlassProvider size="sm" density="compact" locale="ko-KR">
  <App />
</PlassProvider>;

It deliberately does not set variant or elevation: those name what a surface is made of and how far off the page it sits, and both are decided per component by the design language. A button is solid and rests on the sheet, a field is cut into it. The guide has the long version, and the precedence: a component's own prop beats the set it is in, which beats the provider.

The Flutter package does the same through PlassTheme.merge, which also carries the date vocabulary. names and labels are what a locale is there, since the framework ships no Intl.

Hooks

React-only, and the machinery the library already ran on rather than anything new. Import them from the barrel or from plass-ui/hooks.

Hook What it answers
usePlMediaQuery Whether the window matches a CSS media query, re-rendering when it stops
usePlBreakpoint Which rung of the breakpoint ladder the window is on
usePlBreakpointValue What a PlassResponsive map resolves to at that rung
usePlReducedMotion Whether the reader has asked their platform for less movement
usePlHotKeys Binds keyboard chords, spelled the way PlHotKeys draws them
usePlColorScheme The dark mode toggle, the choice, where it is kept, and what it writes
usePlDisclosure One boolean and the four callbacks that change it, all of them stable
usePlElementSize The size of an element's content box, kept up to date as it changes
usePlOnScreen Whether an element has been seen, or is on screen now with once: false

usePlassDefaults, usePlToast and usePlConfirm live with the part they belong to rather than in plass-ui/hooks. usePlassDefaults returns what the nearest PlassProvider decided, and is imported from the barrel or from plass-ui/provider. usePlToast raises a toast from a click handler under a PlToastProvider, and is imported from the barrel or from plass-ui/toast. usePlConfirm asks a question from a click handler under a PlConfirmProvider and waits for the answer, and is imported from the barrel or from plass-ui/confirm.

Flutter answers the same questions with framework calls (MediaQuery, PlassTheme) rather than with anything this package would add. Each hook's page names the Dart equivalent.

Development

One design language, three things built out of it. Each is entered and run on its own. There is no install at the repository root and no root package.json.

cd packages/react
npm install
npm test              # Vitest, three shards of headless Chromium
npm run typecheck     # tsc --noEmit over both TS projects
npm run build         # tsc + terser + build-styles → dist/
npm run lint          # ESLint
cd packages/flutter
flutter pub get
flutter test          # Widget tests
flutter analyze
cd example && flutter run   # The gallery, on any device
cd docs
npm install
npm run flutter:demos # Compiles the gallery into public/flutter (needs the Flutter SDK)
npm run dev           # VitePress — the develop-and-eyeball loop
npm run build         # Reuses public/flutter when a build is already there

The site renders the React components from packages/react/src through a Vite alias and embeds the Flutter gallery as a frame per preview, so npm run dev is the develop-and-eyeball loop for both; there is no separate demo app. Editing a component shows up immediately on the React side. The Flutter side has to be rebuilt with npm run flutter:demos -- --force: without --force the script skips the build whenever public/flutter already holds one, and npm run build reuses that build the same way.

Contributing

Anyone can contribute to the project by reporting new issues or submitting a pull request. CONTRIBUTING.md is the rest: where things live, and how a change to a component is expected to arrive. Participation is subject to the Code of Conduct.

To report a security issue, please follow the process described in SECURITY.md.

For anything else, reach the maintainers at cdget.com/contact.

License

MIT © CDGet

About

Every surface answers a single question — is this pressed, or does it hold something? A thing you press is tinted glass: a gradient that turns through its colour family rather than darkening, a shadow thrown in that same colour, and a bloom of light that follows your pointer across it.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Sponsor this project

Contributors

Languages