The pale pastel tints washed out to grey over the cream canvas. Use saturated cherry-blossom colors, add a luminous centre + soft base shadow per petal, and render at full opacity so they read as colorful. Claude-Session: https://claude.ai/code/session_016Yr6jELuRc7hyzYLccQKZd
436 lines
13 KiB
CSS
436 lines
13 KiB
CSS
@import "tailwindcss";
|
|
|
|
/* Petal design tokens — soft, warm, bubbly stationery aesthetic.
|
|
Defined in @theme so they generate Tailwind utilities (bg-bg, text-plum,
|
|
rounded-card, font-ui, etc.). See petal-spec.md → Design System. */
|
|
@theme {
|
|
/* Palette */
|
|
--color-bg: #FDF6F0; /* warm cream canvas */
|
|
--color-surface: #FFFFFF; /* card/panel surfaces */
|
|
--color-surface-alt: #FFF0F5; /* rose-tinted alt surface */
|
|
--color-border: #F0E0EB; /* soft pink-grey border */
|
|
--color-plum: #3D2E39; /* warm dark plum text (not harsh black) */
|
|
--color-muted: #9B8CA3; /* muted lavender-grey */
|
|
--color-accent: #E8A0BF; /* soft rose — primary interactive */
|
|
--color-accent-hover: #D98AAF;/* deeper rose on hover */
|
|
|
|
/* Suggestion type colors */
|
|
--color-mint: #A8D8C8; /* grammar */
|
|
--color-peach: #F4B8A0; /* phrasing */
|
|
--color-lavender: #C5B4E8; /* idiom */
|
|
--color-sky: #A8CCE8; /* clarity */
|
|
--color-honey: #CE9B4F; /* voice */
|
|
--color-success: #8FCFA8; /* saved / accepted */
|
|
|
|
/* Typography */
|
|
--font-ui: "Nunito", "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif;
|
|
--font-body: "Lora", Georgia, serif;
|
|
--font-mono: "JetBrains Mono", ui-monospace, monospace;
|
|
|
|
/* Shape language */
|
|
--radius-card: 16px;
|
|
--radius-modal: 24px;
|
|
--radius-pill: 999px;
|
|
--radius-input: 12px;
|
|
|
|
/* Soft, warm, lifted shadow */
|
|
--shadow-soft: 0 4px 20px rgba(180, 130, 160, 0.12);
|
|
}
|
|
|
|
html, body, #root {
|
|
height: 100%;
|
|
}
|
|
|
|
body {
|
|
margin: 0;
|
|
background: var(--color-bg);
|
|
color: var(--color-plum);
|
|
font-family: var(--font-ui);
|
|
-webkit-font-smoothing: antialiased;
|
|
}
|
|
|
|
/* Gentle, consistent motion across interactive elements */
|
|
button, a, input {
|
|
transition: all 200ms ease;
|
|
}
|
|
|
|
/* Editor body — serif Lora for prose, warm plum ink, roomy line height. The
|
|
title and headings use the Nunito UI face for contrast. */
|
|
.petal-prose {
|
|
font-family: var(--font-body);
|
|
font-size: 1.125rem;
|
|
line-height: 1.75;
|
|
color: var(--color-plum);
|
|
}
|
|
.petal-prose > * + * {
|
|
margin-top: 0.9em;
|
|
}
|
|
.petal-prose h1,
|
|
.petal-prose h2,
|
|
.petal-prose h3 {
|
|
font-family: var(--font-ui);
|
|
font-weight: 800;
|
|
line-height: 1.3;
|
|
}
|
|
.petal-prose h1 { font-size: 1.6em; }
|
|
.petal-prose h2 { font-size: 1.3em; }
|
|
.petal-prose h3 { font-size: 1.1em; }
|
|
.petal-prose ul,
|
|
.petal-prose ol {
|
|
padding-left: 1.4em;
|
|
}
|
|
.petal-prose ul { list-style: disc; }
|
|
.petal-prose ol { list-style: decimal; }
|
|
.petal-prose a {
|
|
color: var(--color-accent-hover);
|
|
text-decoration: underline;
|
|
}
|
|
.petal-prose blockquote {
|
|
border-left: 3px solid var(--color-border);
|
|
padding-left: 1em;
|
|
color: var(--color-muted);
|
|
font-style: italic;
|
|
}
|
|
.petal-prose code {
|
|
font-family: var(--font-mono);
|
|
font-size: 0.9em;
|
|
background: var(--color-surface-alt);
|
|
padding: 0.1em 0.35em;
|
|
border-radius: 6px;
|
|
}
|
|
/* Placeholder shown on the empty first paragraph (Tiptap Placeholder ext). */
|
|
.petal-prose p.is-editor-empty:first-child::before {
|
|
content: attr(data-placeholder);
|
|
color: var(--color-muted);
|
|
float: left;
|
|
height: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* --- Suggestion decorations -------------------------------------------------
|
|
Inline highlights anchored by string at render time (not stored marks). Each
|
|
type gets a soft underline in its palette color; hovering opens its card. */
|
|
.petal-suggestion {
|
|
border-bottom: 2px solid transparent;
|
|
border-radius: 2px;
|
|
cursor: pointer;
|
|
transition: background 200ms ease;
|
|
/* gentle fade + slight upward float as decorations appear */
|
|
animation: petal-suggestion-in 260ms ease both;
|
|
}
|
|
.petal-suggestion:hover {
|
|
background: var(--color-surface-alt);
|
|
}
|
|
.petal-suggestion-grammar { border-bottom-color: var(--color-mint); }
|
|
.petal-suggestion-phrasing { border-bottom-color: var(--color-peach); }
|
|
.petal-suggestion-idiom { border-bottom-color: var(--color-lavender); }
|
|
.petal-suggestion-clarity { border-bottom-color: var(--color-sky); }
|
|
.petal-suggestion-voice { border-bottom-color: var(--color-honey); }
|
|
|
|
@keyframes petal-suggestion-in {
|
|
from { opacity: 0; transform: translateY(4px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
.petal-suggestion-card {
|
|
animation: petal-suggestion-in 200ms ease both;
|
|
}
|
|
|
|
/* --- Spell check ------------------------------------------------------------
|
|
Browser-side nspell flags misspellings with a soft rose wavy underline (a
|
|
gentler take on the classic red squiggle, to fit the pastel palette). Like the
|
|
suggestion layer these are ProseMirror decorations, never stored marks.
|
|
Clicking a flagged word opens its MisspellCard with corrections. */
|
|
.petal-misspelling {
|
|
cursor: pointer;
|
|
text-decoration: underline wavy var(--color-accent);
|
|
text-decoration-skip-ink: none;
|
|
text-underline-offset: 2px;
|
|
}
|
|
.petal-misspelling:hover {
|
|
background: var(--color-surface-alt);
|
|
border-radius: 2px;
|
|
}
|
|
|
|
.petal-misspell-card {
|
|
animation: petal-suggestion-in 200ms ease both;
|
|
}
|
|
|
|
/* --- Word lookup popover ----------------------------------------------------
|
|
Right-click a word for its dictionary definition and synonyms. Same soft card
|
|
treatment as the spelling popover; synonym pills swap the word on click. */
|
|
.petal-word-card {
|
|
animation: petal-suggestion-in 200ms ease both;
|
|
}
|
|
|
|
/* --- ESL superpowers (Phase 9) ----------------------------------------------
|
|
Inline Chinese gloss on hover (a small dark tooltip under the word), and the
|
|
selection rewrite bubble + its preview card. All share the soft pop-in. The
|
|
gloss tip fades in a touch faster — it's a fleeting reading aid, not a panel. */
|
|
.petal-gloss-tip {
|
|
animation: petal-suggestion-in 140ms ease both;
|
|
}
|
|
.petal-selection-bubble,
|
|
.petal-rewrite-card {
|
|
animation: petal-suggestion-in 200ms ease both;
|
|
}
|
|
|
|
/* --- Accept confetti --------------------------------------------------------
|
|
A tiny CSS-only burst played where a suggestion is accepted: four colored
|
|
dots spray up-and-out, then fade. No JS animation — each dot reads its
|
|
direction from --dx/--dy custom properties set inline. (Spec → Signature.) */
|
|
.petal-confetti {
|
|
width: 0;
|
|
height: 0;
|
|
}
|
|
.petal-confetti-dot {
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
width: 7px;
|
|
height: 7px;
|
|
border-radius: var(--radius-pill);
|
|
animation: petal-confetti 680ms cubic-bezier(0.2, 0.7, 0.3, 1) forwards;
|
|
}
|
|
@keyframes petal-confetti {
|
|
0% { transform: translate(0, 0) scale(0.3); opacity: 0; }
|
|
18% { opacity: 1; }
|
|
100% { transform: translate(var(--dx), var(--dy)) scale(1); opacity: 0; }
|
|
}
|
|
|
|
/* --- Distraction-free mode ---------------------------------------------------
|
|
The doc-list sidebar slides left and collapses to zero width; the editor
|
|
canvas (centered, max-width) re-centers into the full pane. Width + transform
|
|
animate together for a smooth slide. (Spec → Distraction-free mode.) */
|
|
.petal-sidebar {
|
|
width: 260px;
|
|
overflow: hidden;
|
|
transition: width 280ms ease, transform 280ms ease, opacity 200ms ease;
|
|
}
|
|
.petal-sidebar-hidden {
|
|
width: 0;
|
|
transform: translateX(-24px);
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* --- Companion kitten -------------------------------------------------------
|
|
The cozy corner mascot. Gently bobs while awake, settles and sways slowly
|
|
while napping; its speech bubble pops in; little zzz drift up when asleep. */
|
|
.petal-companion {
|
|
animation: petal-bob 3.2s ease-in-out infinite;
|
|
transition: transform 200ms ease;
|
|
}
|
|
.petal-companion:hover {
|
|
transform: translateY(-2px) scale(1.04);
|
|
}
|
|
.petal-companion-sleep {
|
|
animation: petal-bob-slow 5s ease-in-out infinite;
|
|
}
|
|
@keyframes petal-bob {
|
|
0%, 100% { transform: translateY(0); }
|
|
50% { transform: translateY(-4px); }
|
|
}
|
|
@keyframes petal-bob-slow {
|
|
0%, 100% { transform: translateY(0) rotate(-1deg); }
|
|
50% { transform: translateY(-2px) rotate(1deg); }
|
|
}
|
|
|
|
.petal-bubble {
|
|
animation: petal-bubble-in 240ms cubic-bezier(0.2, 0.8, 0.3, 1.2) both;
|
|
transform-origin: bottom right;
|
|
}
|
|
@keyframes petal-bubble-in {
|
|
from { opacity: 0; transform: translateY(6px) scale(0.92); }
|
|
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
}
|
|
|
|
.petal-zzz {
|
|
top: 6px;
|
|
right: 14px;
|
|
font-weight: 800;
|
|
font-size: 1.1rem;
|
|
animation: petal-zzz 2.4s ease-in-out infinite;
|
|
}
|
|
@keyframes petal-zzz {
|
|
0% { opacity: 0; transform: translateY(0) scale(0.8); }
|
|
30% { opacity: 0.9; }
|
|
100% { opacity: 0; transform: translateY(-14px) scale(1.1); }
|
|
}
|
|
|
|
/* "New version available" banner — drifts down from the top like a petal. */
|
|
.petal-update {
|
|
animation: petal-drop 360ms cubic-bezier(0.2, 0.8, 0.3, 1.2) both;
|
|
}
|
|
@keyframes petal-drop {
|
|
from { opacity: 0; transform: translateY(-14px) scale(0.96); }
|
|
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
}
|
|
|
|
/* Blinking caret in the Ask Petal bubble while awaiting the first token. */
|
|
.petal-chat-caret {
|
|
animation: petal-breathe 1s ease-in-out infinite;
|
|
color: var(--color-accent);
|
|
}
|
|
|
|
/* Breathing rose dot shown in the StatusBar while a checkpoint runs. */
|
|
.petal-checkpoint-dot {
|
|
animation: petal-breathe 2s ease-in-out infinite;
|
|
}
|
|
@keyframes petal-breathe {
|
|
0%, 100% { opacity: 0.4; }
|
|
50% { opacity: 1; }
|
|
}
|
|
|
|
/* --- Organization & search (Phase 10) --------------------------------------
|
|
Tag chips, the search results list, and per-row affordances. The row actions
|
|
(tag / delete) fade in on hover for pointer users but are always visible on
|
|
touch (no hover) — see the coarse-pointer block below. */
|
|
.petal-tag-chip {
|
|
transition: background 160ms ease, color 160ms ease, transform 160ms ease;
|
|
}
|
|
.petal-search-results {
|
|
animation: petal-suggestion-in 160ms ease both;
|
|
}
|
|
.petal-tag-picker {
|
|
animation: petal-suggestion-in 160ms ease both;
|
|
}
|
|
.petal-row-action {
|
|
opacity: 0;
|
|
transition: opacity 160ms ease, background 160ms ease;
|
|
}
|
|
.group:hover .petal-row-action,
|
|
.petal-row-action:focus-visible {
|
|
opacity: 1;
|
|
}
|
|
.petal-row-action:hover {
|
|
background: var(--color-surface-alt);
|
|
}
|
|
/* Clamp a search snippet to two lines. */
|
|
.line-clamp-2 {
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* --- Touch & tablet polish --------------------------------------------------
|
|
On coarse pointers (tablets, phones) there's no hover, so make tap targets
|
|
comfortable and reveal affordances that would otherwise be hover-only. */
|
|
@media (pointer: coarse) {
|
|
.petal-tap {
|
|
min-height: 44px;
|
|
}
|
|
.petal-tap-sm {
|
|
min-height: 36px;
|
|
}
|
|
/* Row actions can't rely on hover — keep them visible and roomy. */
|
|
.petal-row-action {
|
|
opacity: 1;
|
|
height: 36px;
|
|
width: 36px;
|
|
}
|
|
/* Bigger, easier-to-hit suggestion/word/tag pills. */
|
|
.petal-tag-chip {
|
|
padding-top: 0.25rem;
|
|
padding-bottom: 0.25rem;
|
|
}
|
|
}
|
|
|
|
/* --- Responsive sidebar (narrow screens) ------------------------------------
|
|
Below the tablet breakpoint the sidebar becomes an overlay drawer toggled by a
|
|
hamburger in the header, instead of a permanent column. A scrim sits behind it.
|
|
On wide screens the toggle + scrim are hidden and the sidebar is in-flow. */
|
|
.petal-sidebar-toggle {
|
|
display: none;
|
|
}
|
|
.petal-scrim {
|
|
display: none;
|
|
}
|
|
@media (max-width: 768px) {
|
|
.petal-sidebar-toggle {
|
|
display: inline-flex;
|
|
}
|
|
.petal-sidebar {
|
|
position: fixed;
|
|
top: 48px; /* below the 12-height header */
|
|
bottom: 0;
|
|
left: 0;
|
|
z-index: 30;
|
|
background: var(--color-bg);
|
|
box-shadow: var(--shadow-soft);
|
|
}
|
|
/* On mobile the sidebar is hidden by default; the drawer-open class slides it
|
|
in. (Distraction-free's petal-sidebar-hidden still wins to keep it closed.) */
|
|
.petal-sidebar:not(.petal-drawer-open) {
|
|
width: 0;
|
|
transform: translateX(-24px);
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
.petal-scrim.petal-scrim-show {
|
|
display: block;
|
|
position: fixed;
|
|
inset: 48px 0 0 0;
|
|
z-index: 20;
|
|
background: rgba(61, 46, 57, 0.18);
|
|
}
|
|
}
|
|
|
|
/* Print / Save-as-PDF: strip every bit of app chrome and editing decoration so
|
|
only the title and the writing itself reach the page. This is Petal's PDF
|
|
path — it uses the browser's own fonts, so CJK renders correctly with no
|
|
server-side font embedding. */
|
|
/* --- Ambient falling petals -------------------------------------------------
|
|
The WebGL petal layer is a fixed, non-interactive overlay that drifts soft
|
|
blossoms over the page. Kept gentle (low opacity) so it never competes with
|
|
the writing. It hides itself in print and is skipped entirely for users who
|
|
prefer reduced motion (the component renders nothing in that case). */
|
|
.petal-fall {
|
|
opacity: 1;
|
|
}
|
|
|
|
/* Honor reduced-motion globally: still the looping ambient animations and drop
|
|
the petal layer. One-shot entrance/feedback animations are left intact. */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.petal-fall {
|
|
display: none !important;
|
|
}
|
|
.petal-bob,
|
|
.petal-bob-slow,
|
|
.petal-zzz,
|
|
.petal-checkpoint-dot {
|
|
animation: none !important;
|
|
}
|
|
}
|
|
|
|
@media print {
|
|
.petal-fall,
|
|
.petal-no-print,
|
|
.petal-sidebar,
|
|
.petal-suggestion-card,
|
|
.petal-misspell-card,
|
|
.petal-gloss-tip,
|
|
.petal-selection-bubble,
|
|
.petal-rewrite-card,
|
|
.petal-confetti,
|
|
.petal-companion {
|
|
display: none !important;
|
|
}
|
|
html, body, #root {
|
|
height: auto !important;
|
|
overflow: visible !important;
|
|
background: #fff !important;
|
|
}
|
|
/* Drop the in-editor highlight underlines/tints — they're guidance, not ink. */
|
|
.petal-suggestion,
|
|
.petal-misspelling {
|
|
background: none !important;
|
|
text-decoration: none !important;
|
|
border-bottom: none !important;
|
|
}
|
|
/* Let the writing use the full page width. */
|
|
.max-w-\[720px\] { max-width: none !important; }
|
|
}
|