mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-16 02:17:32 +02:00
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:
401
.claude/skills/tailwind-v4.md
Normal file
401
.claude/skills/tailwind-v4.md
Normal 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());
|
||||
```
|
||||
46
CLAUDE.md
46
CLAUDE.md
@@ -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
1346
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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",
|
||||
|
||||
@@ -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: {},
|
||||
};
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
503
src/index.css
503
src/index.css
@@ -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 */
|
||||
|
||||
@@ -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: [],
|
||||
};
|
||||
@@ -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"),
|
||||
|
||||
Reference in New Issue
Block a user