From b6cf54c83693556888432f1336d7c427d6be141d Mon Sep 17 00:00:00 2001 From: ollyhearn Date: Sun, 31 May 2026 16:47:52 +0300 Subject: [PATCH] stuff --- CLAUDE.md | 53 ++++++ README.md | 58 +++++- assets/logo.svg | 21 +++ package-lock.json | 4 +- package.json | 7 +- src/App.tsx | 8 +- src/components/theme.tsx | 2 +- src/components/ui.tsx | 156 ++++++++-------- src/index.ts | 5 +- src/styles/components.css | 364 +++++++++++++++++++------------------- src/styles/fonts.css | 24 +++ src/styles/global.css | 1 + src/styles/index.css | 7 +- src/styles/tokens.css | 39 ++-- 14 files changed, 444 insertions(+), 305 deletions(-) create mode 100644 CLAUDE.md create mode 100644 assets/logo.svg create mode 100644 src/styles/fonts.css diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..ffa4f18 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,53 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this is + +`@modernsk/ui` — a tactile, dark-first React component library built on [Radix](https://www.radix-ui.com/) primitives. Distributed as a git-installable / publishable package; only `dist/` ships. Consumers get built ESM + CJS, `.d.ts` types, and a single `styles.css`. + +## Commands + +```bash +npm run dev # Rsbuild playground at http://localhost:3000 (renders src/App.tsx — every component on one page) +npm run build # build publishable package: tsup (JS/types) + build:css (bundled stylesheet) into dist/ +npm run lint # rslint (rslint.config.ts) +npm run format # prettier --write . +``` + +No test suite exists. `npm run build` runs automatically on install via the `prepare` script — consumers build the package themselves. + +The build is two steps that must both run (the `build` script chains them): +- `tsup` bundles `src/index.ts` → ESM/CJS + types. `react`/`react-dom` are externalized (peer deps); `radix-ui` + `@phosphor-icons/react` are bundled. +- `build:css` runs esbuild on `src/styles/index.css` with `--loader:.ttf=dataurl`, inlining the self-hosted Anta font so no asset hosting is needed. + +## Architecture + +Two parallel surfaces share the same source but never mix at publish time: + +- **Library (shipped):** `src/index.ts` is the public entry. It re-exports everything from `src/components/ui.tsx`, plus `ThemeProvider`/`useTheme` from `src/components/theme.tsx`, and exposes `TooltipProvider` (Radix's `Tooltip.Provider`). The shipped stylesheet entry is `src/styles/index.css`. +- **Playground (dev-only, never published):** `src/index.tsx` mounts `src/App.tsx`, and uses `src/styles/global.css`. These are the Rsbuild dev target only. + +`src/styles/index.css` (shipped) vs `src/styles/global.css` (dev) is a deliberate split: `index.css` imports only `tokens.css` + `components.css` and applies box-sizing at **zero specificity** via `:where([class^='msk-'])` so it never touches consumer elements. `global.css` adds a global reset and kitchen-sink layout helpers — those must stay out of the shipped bundle. + +### Components (`src/components/ui.tsx`) + +All components live in one file. Pattern: Radix provides logic/accessibility, every visual comes from CSS. Components are thin wrappers that attach `msk-*` classes and spread props. The `cx()` helper joins class names (no classnames dependency). Components forward refs where they wrap a DOM element. There is no inline styling and no CSS-in-JS — **all appearance is driven by `msk-*` classes resolving against CSS custom properties.** + +### Styling system (`src/styles/`) + +- `tokens.css` — single source of truth: color/type CSS custom properties, `@font-face`, Google Fonts import. Every component reads from here. +- `components.css` — the `msk-*` class definitions. +- Dark/light is driven by `data-theme` on ``, set by `ThemeProvider` (persisted to `localStorage` under key `msk-theme`, default `dark`). + +When adding or changing a component: add the wrapper in `ui.tsx`, define its `msk-*` styles in `components.css`, and pull any new color/spacing value from a token in `tokens.css` rather than hardcoding. + +## Consumer contract + +Consumers import `@modernsk/ui/styles.css` once at app root, wrap their tree in `ThemeProvider`, and wrap any tooltip-using subtree in `TooltipProvider`. Keep these provider requirements intact when refactoring exports. + +## Conventions + +- React 19, React Compiler is enabled (`babel-plugin-react-compiler` in the Rsbuild dev pipeline). +- TypeScript is `noEmit` + `verbatimModuleSyntax`: use `import type` for type-only imports. `noUnusedLocals`/`noUnusedParameters` are on. +- ESM-only package (`"type": "module"`). diff --git a/README.md b/README.md index ed3ca07..5e82ed5 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,21 @@ -# @modernsk/ui +
+ +ModernSK + +**Tactile, dark-first React component library built on [Radix](https://www.radix-ui.com/) primitives.** -Tactile, dark-first React component library built on [Radix](https://www.radix-ui.com/) primitives. Old-iOS skeuomorphism × macOS Sequoia neatness × Ubuntu warmth. +
+ +--- + ## Install -Git-hosted (pin to a tag): +Distributed via self-hosted git — install straight from the repo: ```bash -npm install github:YOUR_ORG/modernsk-ui#v0.1.0 +npm i git+ssh://git@git.ollyhearn.ru:49239/olly/modern-sk.git ``` `react` and `react-dom` (>=18) are peer dependencies — your app provides them. @@ -19,8 +26,9 @@ The package builds itself on install via the `prepare` script. Import the stylesheet once at your app root, then use components anywhere: ```tsx -import '@modernsk/ui/styles.css'; -import { ThemeProvider, TooltipProvider, Button, Card } from '@modernsk/ui'; +import 'modern-sk/styles.css'; // required — tokens + components +import 'modern-sk/fonts.css'; // optional — branded faces (see Fonts) +import { ThemeProvider, TooltipProvider, Button, Card } from 'modern-sk'; export function App() { return ( @@ -37,7 +45,31 @@ export function App() { - `ThemeProvider` manages dark/light via `data-theme` on `` and persists to `localStorage`. Read it with `useTheme()`. - `TooltipProvider` must wrap any tree that uses ``. -- All visuals come from CSS custom properties in the shipped stylesheet; the self-hosted display font is inlined, so no extra asset hosting is needed. +- All visuals come from CSS custom properties in the shipped stylesheet. + +## Fonts + +`modern-sk/styles.css` ships **no fonts**. The type tokens default to a chain +that degrades to `system-ui`, so the library works with zero font loading. + +To get the branded ModernSK faces (Anta display + Onest + Geist Mono), import +the optional stylesheet — Anta is self-hosted and inlined, no asset hosting +needed: + +```tsx +import 'modern-sk/fonts.css'; +``` + +To use your **own** fonts, skip `fonts.css` and override the tokens anywhere +after `styles.css` — every component re-reads them: + +```css +:root { + --font-display: 'Your Display', sans-serif; + --font-sans: 'Inter', system-ui, sans-serif; + --font-mono: 'JetBrains Mono', ui-monospace, monospace; +} +``` ## Develop @@ -51,4 +83,14 @@ npm run lint ## What ships -`npm publish` / git-install exposes only `dist/` — built ESM + CJS, `.d.ts` types, and `styles.css`. The playground (`src/App.tsx`, Rsbuild config) is dev-only and never published. +A git install exposes only `dist/` — built ESM + CJS, `.d.ts` types, +`styles.css`, and `fonts.css`. The playground (`src/App.tsx`, Rsbuild config) +is dev-only and never published. + +--- + +
+ +brought to you by **ollyhearn** & **claude** with <3 + +
diff --git a/assets/logo.svg b/assets/logo.svg new file mode 100644 index 0000000..3ffdbcc --- /dev/null +++ b/assets/logo.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + MODERNSK + + diff --git a/package-lock.json b/package-lock.json index 57142ac..89c4ba0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "@modernsk/ui", + "name": "modern-sk", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "@modernsk/ui", + "name": "modern-sk", "version": "0.1.0", "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 3b9e910..eae1c18 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "@modernsk/ui", + "name": "modern-sk", "version": "0.1.0", "description": "ModernSK — tactile, dark-first React component library built on Radix primitives.", "license": "MIT", @@ -16,14 +16,15 @@ "import": "./dist/index.js", "require": "./dist/index.cjs" }, - "./styles.css": "./dist/styles.css" + "./styles.css": "./dist/styles.css", + "./fonts.css": "./dist/fonts.css" }, "files": [ "dist" ], "scripts": { "build": "tsup && npm run build:css", - "build:css": "esbuild src/styles/index.css --bundle --loader:.ttf=dataurl --outfile=dist/styles.css", + "build:css": "esbuild src/styles/index.css --bundle --outfile=dist/styles.css && esbuild src/styles/fonts.css --bundle --loader:.ttf=dataurl --outfile=dist/fonts.css", "dev": "rsbuild dev --open", "preview": "rsbuild preview", "lint": "rslint", diff --git a/src/App.tsx b/src/App.tsx index e86a3c1..bb582ca 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -76,7 +76,7 @@ const App = () => { const [chips, setChips] = useState(['design', '2026', 'invoices']); return ( -
+
@@ -87,8 +87,8 @@ const App = () => {

Every component, live and interactive, built on Radix Primitives styled from the global tokens in{' '} - tokens.css +{' '} - components.css. Click, toggle, + tokens.css +{' '} + components.css. Click, toggle, focus — it all responds.

@@ -242,7 +242,7 @@ const App = () => { onDecrement={() => setCount((n) => Math.max(0, n - 1))} onIncrement={() => setCount((n) => n + 1)} /> - + {count}
diff --git a/src/components/theme.tsx b/src/components/theme.tsx index 6166545..f1aa351 100644 --- a/src/components/theme.tsx +++ b/src/components/theme.tsx @@ -7,7 +7,7 @@ import { } from 'react'; type ThemeMode = 'dark' | 'light'; -const KEY = 'msk-theme'; +const KEY = 'modern-sk-theme'; const ThemeContext = createContext<{ theme: ThemeMode; diff --git a/src/components/ui.tsx b/src/components/ui.tsx index 91ebbc3..021b4de 100644 --- a/src/components/ui.tsx +++ b/src/components/ui.tsx @@ -42,10 +42,10 @@ export const Button = forwardRef(