Migrate from Tailwind CSS v3 to v4

- Replace JS config (tailwind.config.js) with CSS-first @theme directive
- Add @tailwindcss/vite plugin for improved Vite integration
- Update src/index.css with v4 syntax (@import, @theme, @utility)
- Convert @layer utilities to @utility syntax
- Fix hardcoded scrollbar colors in command-launcher.css
- Add Tailwind v4 skill document (.claude/skills/tailwind-v4.md)
- Update CLAUDE.md with Tailwind v4 quick reference

https://claude.ai/code/session_01T6RenqDof8br6Nt9aKcjvq
This commit is contained in:
Claude
2026-01-27 09:38:12 +00:00
parent 34bad20ce9
commit ee85337e4b
9 changed files with 1328 additions and 1072 deletions

View File

@@ -0,0 +1,401 @@
# Skill: Tailwind CSS v4
This document provides guidance for writing CSS and using Tailwind in Grimoire after the v4 migration.
## Quick Reference
### Import Syntax
```css
/* v4 - Single import replaces @tailwind directives */
@import "tailwindcss";
```
### Defining Theme Variables
```css
@theme {
--color-brand: oklch(0.72 0.11 221.19);
--font-display: "Satoshi", sans-serif;
--animate-fade: fade 0.3s ease-out;
@keyframes fade {
from { opacity: 0; }
to { opacity: 1; }
}
}
```
### Custom Utilities
```css
/* v4 - Use @utility instead of @layer utilities */
@utility content-auto {
content-visibility: auto;
}
/* With nested selectors */
@utility scrollbar-hidden {
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
}
```
### Custom Variants
```css
/* Simple variant */
@custom-variant theme-dark (&:where(.dark, .dark *));
/* Complex variant */
@custom-variant any-hover {
@media (any-hover: hover) {
&:hover {
@slot;
}
}
}
```
---
## Grimoire Theme System
Grimoire uses a **two-level CSS variable system** for runtime theming:
### Level 1: Runtime Variables (set by ThemeProvider)
These are HSL values WITHOUT the `hsl()` wrapper:
```css
:root {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--primary: 210 40% 98%;
/* ... */
}
```
### Level 2: Tailwind Color Mapping (in @theme)
These reference the runtime variables with `hsl()`:
```css
@theme {
--color-background: hsl(var(--background));
--color-foreground: hsl(var(--foreground));
--color-primary: hsl(var(--primary));
}
```
### Using Colors in Components
**In Tailwind classes:**
```tsx
<div className="bg-background text-foreground border-border">
<button className="bg-primary text-primary-foreground">
<span className="text-muted-foreground">
```
**In custom CSS with opacity:**
```css
.my-element {
background: hsl(var(--primary) / 0.1);
border: 1px solid hsl(var(--border) / 0.5);
}
```
### Available Color Tokens
| Token | Usage |
|-------|-------|
| `background` / `foreground` | Page background and text |
| `card` / `card-foreground` | Card surfaces |
| `popover` / `popover-foreground` | Dropdowns, tooltips |
| `primary` / `primary-foreground` | Primary buttons, links |
| `secondary` / `secondary-foreground` | Secondary actions |
| `accent` / `accent-foreground` | Highlights, emphasis |
| `muted` / `muted-foreground` | Subdued elements |
| `destructive` / `destructive-foreground` | Delete, error actions |
| `border` | Borders |
| `input` | Form input borders |
| `ring` | Focus rings |
| `success` / `warning` / `info` | Status indicators |
| `zap` | Lightning zap color (gold) |
| `live` | Live indicator (red) |
| `highlight` | Active user highlight (orange) |
| `tooltip` / `tooltip-foreground` | Tooltip background/text |
---
## Container Queries (Built-in)
No plugin needed in v4. Use for component-relative responsiveness:
```tsx
// Parent defines container
<div className="@container">
// Children respond to container width
<div className="grid grid-cols-1 @sm:grid-cols-2 @lg:grid-cols-3">
{items.map(item => <Card key={item.id} />)}
</div>
</div>
```
### Container Query Breakpoints
| Variant | Width |
|---------|-------|
| `@xs:` | 20rem (320px) |
| `@sm:` | 24rem (384px) |
| `@md:` | 28rem (448px) |
| `@lg:` | 32rem (512px) |
| `@xl:` | 36rem (576px) |
| `@2xl:` | 42rem (672px) |
### Max-width Queries
```tsx
<div className="@container">
<div className="flex-row @max-sm:flex-col">
{/* Row on larger containers, column on smaller */}
</div>
</div>
```
### Named Containers
```tsx
<div className="@container/sidebar">
<nav className="@sm/sidebar:flex-col">
```
---
## Renamed Utilities (v3 → v4)
| v3 | v4 | Notes |
|----|----| ------|
| `shadow-sm` | `shadow-xs` | Smallest shadow |
| `shadow` | `shadow-sm` | Default shadow |
| `rounded-sm` | `rounded-xs` | Smallest radius |
| `rounded` | `rounded-sm` | Default radius |
| `blur-sm` | `blur-xs` | Smallest blur |
| `blur` | `blur-sm` | Default blur |
| `ring` | `ring-3` | Default ring width |
**Important:** Always use the named size, not bare utilities.
---
## CSS Variable Syntax in Classes
**v4 uses parentheses instead of brackets:**
```tsx
// ❌ v3 syntax (deprecated)
<div className="bg-[--my-color]" />
<div className="fill-[--icon-color]" />
// ✅ v4 syntax
<div className="bg-(--my-color)" />
<div className="fill-(--icon-color)" />
```
---
## Important Modifier Position
**v4 moves `!` to the end:**
```tsx
// ❌ v3 syntax
<div className="!flex !mt-0" />
// ✅ v4 syntax
<div className="flex! mt-0!" />
```
---
## New Useful Variants
### not-* Variant
Style when condition is NOT met:
```tsx
<div className="not-hover:opacity-75">Dims when not hovered</div>
<div className="not-first:mt-4">Margin except first child</div>
<div className="not-disabled:cursor-pointer">Clickable when enabled</div>
```
### @starting-style for Animations
CSS-only enter animations without JS:
```tsx
<dialog className="
transition-all duration-300
open:opacity-100 open:scale-100
starting:open:opacity-0 starting:open:scale-95
">
```
### inert Variant
Style non-interactive elements:
```tsx
<div className="inert:opacity-50 inert:pointer-events-none">
```
---
## 3D Transforms
```tsx
<div className="perspective-distant">
<div className="rotate-x-12 rotate-y-6 transform-3d hover:rotate-y-12">
3D card effect
</div>
</div>
```
Available utilities:
- `rotate-x-*`, `rotate-y-*`, `rotate-z-*`
- `translate-z-*`, `scale-z-*`
- `perspective-normal` (500px), `perspective-distant` (1200px)
- `transform-3d` (enables 3D space)
---
## Gradients
### Linear Gradients
```tsx
// Angle-based
<div className="bg-linear-45 from-red-500 to-blue-500" />
// Direction-based
<div className="bg-linear-to-r from-primary to-accent" />
// With color interpolation (more vivid colors)
<div className="bg-linear-to-r/oklch from-red-500 to-blue-500" />
```
### Radial & Conic
```tsx
<div className="bg-radial from-white to-transparent" />
<div className="bg-conic from-red-500 via-yellow-500 to-red-500" />
```
---
## Writing Custom CSS
### In @layer base
For global resets and element defaults:
```css
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground font-mono;
}
button:focus-visible {
outline: 2px solid hsl(var(--ring));
outline-offset: 2px;
}
}
```
### With @utility
For reusable utility classes:
```css
@utility text-balance {
text-wrap: balance;
}
@utility glass {
background: hsl(var(--background) / 0.8);
backdrop-filter: blur(8px);
}
```
### Plain CSS (non-Tailwind)
For third-party component overrides (like Mosaic):
```css
/* No @layer needed - uses native cascade */
.mosaic-window .mosaic-window-toolbar {
background: hsl(var(--muted));
border-bottom: 1px solid hsl(var(--border));
}
```
---
## Best Practices
### DO:
- Use semantic color tokens (`bg-primary`) not raw colors
- Use container queries for component responsiveness
- Use `@utility` for custom utilities
- Use HSL variable pattern with opacity: `hsl(var(--color) / 0.5)`
- Keep custom CSS minimal - prefer Tailwind classes
### DON'T:
- Hardcode colors (`bg-blue-500`) - use theme tokens
- Use viewport queries when container queries work better
- Use `@layer utilities` (v3 syntax) - use `@utility`
- Use bracket syntax for CSS vars (`[--var]`) - use parentheses `(--var)`
- Put `!` at the start (`!flex`) - put at end (`flex!`)
---
## File Structure
```
src/
├── index.css # Main CSS: @import "tailwindcss", @theme, @utility, custom CSS
├── styles/
│ └── prism-theme.css # Syntax highlighting (uses CSS variables)
├── components/
│ └── command-launcher.css # Command palette styles
└── lib/themes/
├── types.ts # Theme TypeScript types
├── apply.ts # Runtime theme application
└── builtin/ # Built-in theme definitions
```
---
## Theme Development
When creating or modifying themes, edit files in `src/lib/themes/builtin/`:
```typescript
// src/lib/themes/builtin/my-theme.ts
import type { Theme } from "../types";
export const myTheme: Theme = {
id: "my-theme",
name: "My Theme",
colors: {
background: "220 20% 10%", // HSL without wrapper
foreground: "220 10% 90%",
// ... all required colors
},
syntax: { /* ... */ },
scrollbar: { /* ... */ },
gradient: { /* ... */ },
};
```
Register in `src/lib/themes/builtin/index.ts`.
---
## Quick Debugging
Check what CSS variables are set:
```javascript
// In browser console
getComputedStyle(document.documentElement).getPropertyValue('--background')
```
List all theme variables:
```javascript
import { getThemeVariables } from '@/lib/themes/apply';
console.log(getThemeVariables());
```

View File

@@ -277,10 +277,54 @@ if (canSign) {
- Copy text to clipboard with toast feedback
- Returns: `{ copy, copied }` function and state
## Tailwind CSS v4
Grimoire uses **Tailwind CSS v4** with CSS-first configuration. See `.claude/skills/tailwind-v4.md` for complete reference.
### Quick Reference
**Import (in index.css):**
```css
@import "tailwindcss";
```
**Custom utilities:**
```css
@utility my-utility {
/* styles */
}
```
**Theme colors** - Always use semantic tokens:
```tsx
<div className="bg-background text-foreground">
<button className="bg-primary text-primary-foreground">
<span className="text-muted-foreground">
```
**Container queries** (built-in, no plugin):
```tsx
<div className="@container">
<div className="@sm:grid-cols-2 @lg:grid-cols-3">
```
**Key syntax changes from v3:**
- CSS variables: `bg-(--my-var)` not `bg-[--my-var]`
- Important: `flex!` not `!flex`
- Sizes: `shadow-xs` (was `shadow-sm`), `shadow-sm` (was `shadow`)
### Runtime Theming
Colors use two-level CSS variables:
1. Runtime vars (HSL without wrapper): `--background: 222.2 84% 4.9%`
2. Tailwind mapping: `--color-background: hsl(var(--background))`
This allows `applyTheme()` to switch themes at runtime.
## Key Conventions
- **Path Alias**: `@/` = `./src/`
- **Styling**: Tailwind + HSL CSS variables (theme tokens defined in `index.css`)
- **Styling**: Tailwind v4 + HSL CSS variables (theme tokens defined in `index.css`)
- **Types**: Prefer types from `applesauce-core`, extend in `src/types/` when needed
- **Locale-Aware Formatting** (`src/hooks/useLocale.ts`): All date, time, number, and currency formatting MUST use the user's locale:
- **`useLocale()` hook**: Returns `{ locale, language, region, timezone, timeFormat }` - use in components that need locale config

1346
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -101,7 +101,7 @@
"@types/ws": "^8.18.1",
"@vitejs/plugin-react": "^4.3.4",
"@vitest/ui": "^4.0.15",
"autoprefixer": "^10.4.20",
"@tailwindcss/vite": "^4.0.0",
"eslint": "^9.17.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.4",
@@ -114,7 +114,7 @@
"postcss": "^8.4.49",
"prettier": "^3.7.4",
"sharp": "^0.34.5",
"tailwindcss": "^3.4.17",
"tailwindcss": "^4.0.0",
"typescript": "~5.6.2",
"typescript-eslint": "^8.18.2",
"vite": "^6.0.5",

View File

@@ -1,6 +1,5 @@
// PostCSS config - minimal since we use @tailwindcss/vite plugin
// This file can be removed if no other PostCSS plugins are needed
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
plugins: {},
};

View File

@@ -192,9 +192,9 @@
}
.command-list::-webkit-scrollbar-thumb {
background-color: rgba(255, 255, 255, 0.2);
background-color: hsl(var(--scrollbar-thumb));
}
.command-list::-webkit-scrollbar-thumb:hover {
background-color: rgba(255, 255, 255, 0.3);
background-color: hsl(var(--scrollbar-thumb-hover));
}

View File

@@ -1,11 +1,262 @@
/* Tailwind CSS v4 - CSS-first configuration */
@import "tailwindcss";
/* Prism syntax highlighting theme */
@import "./styles/prism-theme.css";
@tailwind base;
@tailwind components;
@tailwind utilities;
/* ==========================================================================
Theme Configuration (@theme)
These define Tailwind's design tokens - colors, fonts, animations, etc.
They reference the runtime CSS variables set by ThemeProvider.
========================================================================== */
@theme {
/* Font Family */
--font-mono: "Oxygen Mono", monospace;
/* Border Radius - using CSS variable for runtime theming */
--radius-lg: var(--radius);
--radius-md: calc(var(--radius) - 2px);
--radius-sm: calc(var(--radius) - 4px);
/* Animations */
--animate-accordion-down: accordion-down 0.2s ease-out;
--animate-accordion-up: accordion-up 0.2s ease-out;
--animate-skeleton-pulse: skeleton-pulse 1.5s ease-in-out infinite;
@keyframes accordion-down {
from {
height: 0;
}
to {
height: var(--radix-accordion-content-height);
}
}
@keyframes accordion-up {
from {
height: var(--radix-accordion-content-height);
}
to {
height: 0;
}
}
@keyframes skeleton-pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
/* ==========================================================================
Color Tokens
These map runtime CSS variables (set by ThemeProvider) to Tailwind colors.
The runtime variables use HSL values WITHOUT the hsl() wrapper.
========================================================================== */
/* Core Colors */
--color-background: hsl(var(--background));
--color-foreground: hsl(var(--foreground));
/* Card */
--color-card: hsl(var(--card));
--color-card-foreground: hsl(var(--card-foreground));
/* Popover */
--color-popover: hsl(var(--popover));
--color-popover-foreground: hsl(var(--popover-foreground));
/* Primary */
--color-primary: hsl(var(--primary));
--color-primary-foreground: hsl(var(--primary-foreground));
/* Secondary */
--color-secondary: hsl(var(--secondary));
--color-secondary-foreground: hsl(var(--secondary-foreground));
/* Accent */
--color-accent: hsl(var(--accent));
--color-accent-foreground: hsl(var(--accent-foreground));
/* Muted */
--color-muted: hsl(var(--muted));
--color-muted-foreground: hsl(var(--muted-foreground));
/* Destructive */
--color-destructive: hsl(var(--destructive));
--color-destructive-foreground: hsl(var(--destructive-foreground));
/* Form Elements */
--color-border: hsl(var(--border));
--color-input: hsl(var(--input));
--color-ring: hsl(var(--ring));
/* Status Colors */
--color-success: hsl(var(--success));
--color-warning: hsl(var(--warning));
--color-info: hsl(var(--info));
/* Nostr-specific Colors */
--color-zap: hsl(var(--zap));
--color-live: hsl(var(--live));
--color-highlight: hsl(var(--highlight));
/* Tooltip */
--color-tooltip: hsl(var(--tooltip));
--color-tooltip-foreground: hsl(var(--tooltip-foreground));
}
/* ==========================================================================
Runtime Theme Variables
These are HSL values WITHOUT the hsl() wrapper, allowing alpha transparency.
Set dynamically by ThemeProvider via applyTheme().
========================================================================== */
:root {
/* Core colors - light theme defaults (overridden by ThemeProvider) */
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
/* Status colors */
--success: 142 76% 36%;
--warning: 45 93% 47%;
--info: 199 89% 48%;
/* Nostr-specific colors */
--zap: 45 93% 40%;
--live: 0 72% 45%;
/* UI highlight (active user, self-references) */
--highlight: 25 90% 35%;
/* Tooltip colors */
--tooltip: 222.2 47.4% 11.2%;
--tooltip-foreground: 210 40% 98%;
/* Syntax highlighting */
--syntax-comment: 215.4 16.3% 46.9%;
--syntax-punctuation: 222.2 84% 30%;
--syntax-property: 222.2 47.4% 11.2%;
--syntax-string: 142 60% 30%;
--syntax-keyword: 270 80% 50%;
--syntax-function: 222.2 47.4% 11.2%;
--syntax-variable: 222.2 84% 4.9%;
--syntax-operator: 222.2 84% 20%;
/* Diff colors */
--diff-inserted: 142 60% 30%;
--diff-inserted-bg: 142 60% 50% / 0.15;
--diff-deleted: 0 70% 45%;
--diff-deleted-bg: 0 70% 50% / 0.15;
--diff-meta: 199 80% 40%;
--diff-meta-bg: 199 80% 50% / 0.1;
/* Scrollbar */
--scrollbar-thumb: 222.2 84% 4.9% / 0.2;
--scrollbar-thumb-hover: 222.2 84% 4.9% / 0.3;
--scrollbar-track: 0 0% 0% / 0;
/* Gradient colors (RGB values) */
--gradient-1: 234 179 8;
--gradient-2: 249 115 22;
--gradient-3: 147 51 234;
--gradient-4: 6 182 212;
}
/* Dark theme - applied via .dark class or ThemeProvider */
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 70%;
--accent: 270 100% 70%;
--accent-foreground: 222.2 84% 4.9%;
--destructive: 0 75% 75%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
/* Status colors */
--success: 142 76% 46%;
--warning: 38 92% 60%;
--info: 199 89% 58%;
/* Nostr-specific colors */
--zap: 45 93% 58%;
--live: 0 72% 51%;
/* UI highlight (active user, self-references) */
--highlight: 27 96% 61%;
/* Tooltip colors */
--tooltip: 217.2 32.6% 30%;
--tooltip-foreground: 210 40% 98%;
/* Syntax highlighting */
--syntax-comment: 215 20.2% 70%;
--syntax-punctuation: 210 40% 70%;
--syntax-property: 210 40% 98%;
--syntax-string: 215 20.2% 70%;
--syntax-keyword: 210 40% 98%;
--syntax-function: 210 40% 98%;
--syntax-variable: 210 40% 98%;
--syntax-operator: 210 40% 98%;
/* Diff colors */
--diff-inserted: 134 60% 76%;
--diff-inserted-bg: 145 63% 42% / 0.1;
--diff-deleted: 0 100% 76%;
--diff-deleted-bg: 0 100% 60% / 0.1;
--diff-meta: 190 77% 70%;
--diff-meta-bg: 190 77% 70% / 0.08;
/* Scrollbar */
--scrollbar-thumb: 0 0% 100% / 0.2;
--scrollbar-thumb-hover: 0 0% 100% / 0.3;
--scrollbar-track: 0 0% 0% / 0;
/* Gradient colors (RGB values) */
--gradient-1: 250 204 21;
--gradient-2: 251 146 60;
--gradient-3: 168 85 247;
--gradient-4: 34 211 238;
}
/* ==========================================================================
Custom Scrollbar Styling
========================================================================== */
/* Custom scrollbar styling - uses theme variables */
* {
scrollbar-width: thin;
scrollbar-color: hsl(var(--scrollbar-thumb)) hsl(var(--scrollbar-track));
@@ -29,144 +280,9 @@
background-color: hsl(var(--scrollbar-thumb-hover));
}
@layer base {
:root {
/* Core colors - light theme defaults (overridden by ThemeProvider) */
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
/* Status colors */
--success: 142 76% 36%;
--warning: 45 93% 47%;
--info: 199 89% 48%;
/* Nostr-specific colors */
--zap: 45 93% 40%;
--live: 0 72% 45%;
/* UI highlight (active user, self-references) */
--highlight: 25 90% 35%;
/* Tooltip colors */
--tooltip: 222.2 47.4% 11.2%;
--tooltip-foreground: 210 40% 98%;
/* Syntax highlighting */
--syntax-comment: 215.4 16.3% 46.9%;
--syntax-punctuation: 222.2 84% 30%;
--syntax-property: 222.2 47.4% 11.2%;
--syntax-string: 142 60% 30%;
--syntax-keyword: 270 80% 50%;
--syntax-function: 222.2 47.4% 11.2%;
--syntax-variable: 222.2 84% 4.9%;
--syntax-operator: 222.2 84% 20%;
/* Diff colors */
--diff-inserted: 142 60% 30%;
--diff-inserted-bg: 142 60% 50% / 0.15;
--diff-deleted: 0 70% 45%;
--diff-deleted-bg: 0 70% 50% / 0.15;
--diff-meta: 199 80% 40%;
--diff-meta-bg: 199 80% 50% / 0.1;
/* Scrollbar */
--scrollbar-thumb: 222.2 84% 4.9% / 0.2;
--scrollbar-thumb-hover: 222.2 84% 4.9% / 0.3;
--scrollbar-track: 0 0% 0% / 0;
/* Gradient colors (RGB values) */
--gradient-1: 234 179 8;
--gradient-2: 249 115 22;
--gradient-3: 147 51 234;
--gradient-4: 6 182 212;
}
/* Dark theme - applied via .dark class or ThemeProvider */
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 70%;
--accent: 270 100% 70%;
--accent-foreground: 222.2 84% 4.9%;
--destructive: 0 75% 75%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
/* Status colors */
--success: 142 76% 46%;
--warning: 38 92% 60%;
--info: 199 89% 58%;
/* Nostr-specific colors */
--zap: 45 93% 58%;
--live: 0 72% 51%;
/* UI highlight (active user, self-references) */
--highlight: 27 96% 61%;
/* Tooltip colors */
--tooltip: 217.2 32.6% 30%;
--tooltip-foreground: 210 40% 98%;
/* Syntax highlighting */
--syntax-comment: 215 20.2% 70%;
--syntax-punctuation: 210 40% 70%;
--syntax-property: 210 40% 98%;
--syntax-string: 215 20.2% 70%;
--syntax-keyword: 210 40% 98%;
--syntax-function: 210 40% 98%;
--syntax-variable: 210 40% 98%;
--syntax-operator: 210 40% 98%;
/* Diff colors */
--diff-inserted: 134 60% 76%;
--diff-inserted-bg: 145 63% 42% / 0.1;
--diff-deleted: 0 100% 76%;
--diff-deleted-bg: 0 100% 60% / 0.1;
--diff-meta: 190 77% 70%;
--diff-meta-bg: 190 77% 70% / 0.08;
/* Scrollbar */
--scrollbar-thumb: 0 0% 100% / 0.2;
--scrollbar-thumb-hover: 0 0% 100% / 0.3;
--scrollbar-track: 0 0% 0% / 0;
/* Gradient colors (RGB values) */
--gradient-1: 250 204 21;
--gradient-2: 251 146 60;
--gradient-3: 168 85 247;
--gradient-4: 34 211 238;
}
}
/* ==========================================================================
Base Layer - Global Styles
========================================================================== */
@layer base {
* {
@@ -182,30 +298,46 @@
}
}
@layer utilities {
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
/* ==========================================================================
Custom Utilities (v4 @utility syntax)
========================================================================== */
.text-grimoire-gradient {
background: linear-gradient(
to bottom,
rgb(var(--gradient-1)),
rgb(var(--gradient-2)),
rgb(var(--gradient-3)),
rgb(var(--gradient-4))
);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
@utility no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
/* react-medium-image-zoom theme customization - uses background with opacity */
@utility hide-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
@utility text-grimoire-gradient {
background: linear-gradient(
to bottom,
rgb(var(--gradient-1)),
rgb(var(--gradient-2)),
rgb(var(--gradient-3)),
rgb(var(--gradient-4))
);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
/* ==========================================================================
Third-Party Component Overrides
========================================================================== */
/* react-medium-image-zoom theme customization */
[data-rmiz-modal-overlay] {
background-color: hsl(var(--background) / 0.92) !important;
}
@@ -214,7 +346,10 @@
box-shadow: 0 0 40px hsl(var(--foreground) / 0.2);
}
/* React Mosaic Theme Customization */
/* ==========================================================================
React Mosaic Theme Customization
========================================================================== */
.mosaic.mosaic-blueprint-theme.mosaic-blueprint-theme {
background: hsl(var(--background));
}
@@ -235,16 +370,19 @@ body.animating-layout
.mosaic.mosaic-blueprint-theme.mosaic-blueprint-theme
.mosaic-window
.mosaic-window-toolbar {
background: hsl(var(--background));
background: hsl(var(--muted));
border: none;
border-bottom: 1px solid hsl(var(--border));
border-radius: 0;
color: hsl(var(--foreground));
height: 30px;
}
.mosaic.mosaic-blueprint-theme.mosaic-blueprint-theme
.mosaic-window
.mosaic-window-title {
color: hsl(var(--foreground));
font-family: inherit;
}
.mosaic.mosaic-blueprint-theme.mosaic-blueprint-theme .mosaic-window {
@@ -267,23 +405,6 @@ body.animating-layout
box-shadow: none;
}
.mosaic.mosaic-blueprint-theme.mosaic-blueprint-theme
.mosaic-window
.mosaic-window-toolbar {
background: hsl(var(--muted));
border: none;
border-bottom: 1px solid hsl(var(--border));
color: hsl(var(--foreground));
height: 30px;
}
.mosaic.mosaic-blueprint-theme.mosaic-blueprint-theme
.mosaic-window
.mosaic-window-title {
color: hsl(var(--foreground));
font-family: inherit;
}
.mosaic.mosaic-blueprint-theme.mosaic-blueprint-theme
.mosaic-window
.mosaic-window-body {
@@ -336,7 +457,10 @@ body.animating-layout
margin: -2px 0;
}
/* Accessibility: Focus indicators for keyboard navigation */
/* ==========================================================================
Accessibility: Focus Indicators
========================================================================== */
@layer base {
/* Focus-visible for buttons and interactive elements */
button:focus-visible,
@@ -367,7 +491,10 @@ body.animating-layout
}
}
/* TipTap Editor Styles */
/* ==========================================================================
TipTap Editor Styles
========================================================================== */
.ProseMirror {
min-height: 1.25rem;
line-height: 1.25rem;
@@ -451,16 +578,6 @@ body.animating-layout
background-color: hsl(var(--primary) / 0.15);
}
/* Hide scrollbar utility */
.hide-scrollbar {
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE/Edge */
}
.hide-scrollbar::-webkit-scrollbar {
display: none; /* Chrome/Safari/Opera */
}
/* Hide scrollbar in RichEditor */
.rich-editor .ProseMirror {
scrollbar-width: none; /* Firefox */

View File

@@ -1,86 +0,0 @@
/** @type {import('tailwindcss').Config} */
export default {
darkMode: ["class"],
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {
fontFamily: {
mono: ['"Oxygen Mono"', "monospace"],
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
keyframes: {
"accordion-down": {
from: { height: "0" },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: "0" },
},
"skeleton-pulse": {
"0%, 100%": { opacity: "1" },
"50%": { opacity: "0.5" },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
"skeleton-pulse": "skeleton-pulse 1.5s ease-in-out infinite",
},
colors: {
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
// Status colors
success: "hsl(var(--success))",
warning: "hsl(var(--warning))",
info: "hsl(var(--info))",
// Nostr-specific colors
zap: "hsl(var(--zap))",
live: "hsl(var(--live))",
// UI highlight (active user, self-references)
highlight: "hsl(var(--highlight))",
// Tooltip colors
tooltip: {
DEFAULT: "hsl(var(--tooltip))",
foreground: "hsl(var(--tooltip-foreground))",
},
},
},
},
plugins: [],
};

View File

@@ -1,5 +1,6 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
import path from "node:path";
import { fileURLToPath } from "node:url";
@@ -7,7 +8,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
plugins: [react(), tailwindcss()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),