diff --git a/.impeccable/design.json b/.impeccable/design.json index 93f8012..7db8f07 100644 --- a/.impeccable/design.json +++ b/.impeccable/design.json @@ -1,99 +1,30 @@ { "schemaVersion": 2, - "generatedAt": "2026-08-16T00:00:00Z", + "generatedAt": "2026-08-16T00:00:00.000Z", "title": "Design System: LabPréstamos", "extensions": { "colorMeta": { - "verde-institucional": { - "role": "primary", - "displayName": "Verde Institucional", - "canonical": "oklch(48.5% 0.12 154.8)", - "tonalRamp": [ - "oklch(15% 0.12 154.8)", - "oklch(28% 0.12 154.8)", - "oklch(40% 0.12 154.8)", - "oklch(52% 0.12 154.8)", - "oklch(65% 0.12 154.8)", - "oklch(76% 0.12 154.8)", - "oklch(86% 0.12 154.8)", - "oklch(95% 0.12 154.8)" - ] - }, - "dorado-uabc": { - "role": "secondary", - "displayName": "Dorado UABC", - "canonical": "oklch(72.8% 0.148 74.4)", - "tonalRamp": [ - "oklch(15% 0.148 74.4)", - "oklch(28% 0.148 74.4)", - "oklch(40% 0.148 74.4)", - "oklch(52% 0.148 74.4)", - "oklch(65% 0.148 74.4)", - "oklch(76% 0.148 74.4)", - "oklch(86% 0.148 74.4)", - "oklch(95% 0.148 74.4)" - ] - }, - "superficie": { - "role": "neutral", - "displayName": "Superficie", - "canonical": "oklch(97.3% 0.004 157.2)", - "tonalRamp": [ - "oklch(15% 0.004 157.2)", - "oklch(28% 0.004 157.2)", - "oklch(40% 0.004 157.2)", - "oklch(52% 0.004 157.2)", - "oklch(65% 0.004 157.2)", - "oklch(76% 0.004 157.2)", - "oklch(86% 0.004 157.2)", - "oklch(95% 0.004 157.2)" - ] - }, - "tinta": { - "role": "neutral", - "displayName": "Tinta", - "canonical": "oklch(33.5% 0.033 260.9)", - "tonalRamp": [ - "oklch(15% 0.033 260.9)", - "oklch(28% 0.033 260.9)", - "oklch(40% 0.033 260.9)", - "oklch(52% 0.033 260.9)", - "oklch(65% 0.033 260.9)", - "oklch(76% 0.033 260.9)", - "oklch(86% 0.033 260.9)", - "oklch(95% 0.033 260.9)" - ] - }, - "peligro": { - "role": "danger", - "displayName": "Peligro", - "canonical": "oklch(54.4% 0.186 26)", - "tonalRamp": [ - "oklch(15% 0.186 26)", - "oklch(28% 0.186 26)", - "oklch(40% 0.186 26)", - "oklch(52% 0.186 26)", - "oklch(65% 0.186 26)", - "oklch(76% 0.186 26)", - "oklch(86% 0.186 26)", - "oklch(95% 0.186 26)" - ] - } + "cream": { "role": "neutral", "displayName": "Cream Paper", "canonical": "#f4efea" }, + "frost": { "role": "neutral", "displayName": "Frost White", "canonical": "#ffffff" }, + "chalk": { "role": "neutral", "displayName": "Chalk Gray", "canonical": "#f8f8f7" }, + "ink": { "role": "neutral", "displayName": "Charcoal Ink", "canonical": "#383838" }, + "pencil": { "role": "neutral", "displayName": "Pencil Gray", "canonical": "#a1a1a1" }, + "primary": { "role": "primary", "displayName": "Sky Crayon", "canonical": "#6fc2ff" }, + "secondary": { "role": "secondary", "displayName": "Duck Bill Orange", "canonical": "#ff9538" }, + "danger": { "role": "danger", "displayName": "Coral Sketch (fill)", "canonical": "#f38e84" }, + "danger-text": { "role": "danger", "displayName": "Coral Sketch (text, AA-darkened)", "canonical": "#a83224" }, + "positive": { "role": "success", "displayName": "Mint Sketch", "canonical": "#38c1b0" } }, "typographyMeta": { - "headline": { "displayName": "Headline", "purpose": "Page-level h1 — dashboard/section titles, login title." }, - "title": { "displayName": "Title", "purpose": "Card and section headers within a page." }, - "body": { "displayName": "Body", "purpose": "Default text size across the whole system: table cells, descriptions, list content." }, - "label": { "displayName": "Label", "purpose": "Form field labels, always paired with htmlFor/id." }, - "caption": { "displayName": "Caption", "purpose": "Metadata: matrícula, inventory numbers, timestamps, field hints." } + "body": { "displayName": "Body / Mono", "purpose": "Universal voice — every role through weight, size, and case." }, + "label": { "displayName": "Label", "purpose": "Uppercase structural labels: nav, buttons, form labels, headers." } }, "shadows": [ - { "name": "floating-chrome", "value": "0 10px 30px -8px rgba(0,0,0,0.25)", "purpose": "Mobile dock nav — soft ambient shadow signaling it floats above the scrolling page." }, - { "name": "overlay-notification", "value": "0 16px 40px -12px rgba(0,0,0,0.25)", "purpose": "Toast stack — same ambient quality, slightly heavier, paired with backdrop-blur-xl." } + { "name": "hard", "value": "-6px 6px 0 0 #383838", "purpose": "The system's only shadow — every raised element at rest." }, + { "name": "hard-sm", "value": "-3px 3px 0 0 #383838", "purpose": "Active/pressed state of a raised element (halfway press)." } ], "motion": [ - { "name": "toast-entrance", "value": "cubic-bezier(0.22, 1, 0.36, 1)", "purpose": "260ms translate+scale+fade entrance for toast notifications." }, - { "name": "chrome-transition", "value": "cubic-bezier(0, 0, 0.2, 1)", "purpose": "150-200ms default ease-out for sidebar collapse, hover states, dock icon transitions." } + { "name": "press", "value": "transform: translate(-6px, 6px); transition: transform 100ms ease-out;", "purpose": "Hover/focus on any raised element — presses into its own shadow." } ], "breakpoints": [ { "name": "md", "value": "768px" } @@ -104,100 +35,102 @@ "name": "Primary Button", "kind": "button", "refersTo": "button-primary", - "description": "Default call to action: submit, approve, continue with Google.", - "html": "", - "css": ".ds-btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 44px; padding: 10px 16px; border-radius: 8px; font-weight: 500; font-size: 0.875rem; border: none; cursor: pointer; touch-action: manipulation; transition: filter 150ms ease-out, background-color 150ms ease-out; } .ds-btn-primary { background: #00723F; color: #ffffff; } .ds-btn-primary:hover { filter: brightness(1.1); } .ds-btn-primary:focus-visible { outline: 2px solid #00723F; outline-offset: 2px; } .ds-btn-primary:disabled { opacity: 0.5; pointer-events: none; }" + "description": "The system's default call to action.", + "html": "", + "css": ".ds-btn { display:inline-flex; align-items:center; justify-content:center; gap:8px; min-height:44px; padding:10px 16px; border-radius:2px; border:2px solid #383838; font-family:'JetBrains Mono Variable',monospace; letter-spacing:0.02em; font-weight:500; box-shadow:-6px 6px 0 0 #383838; transition:transform 100ms ease-out; } .ds-btn:hover, .ds-btn:focus-visible { transform:translate(-6px,6px); box-shadow:0 0 0 0 transparent; } .ds-btn-primary { background:#6fc2ff; color:#383838; }" }, { "name": "Secondary Button", "kind": "button", "refersTo": "button-secondary", - "description": "Sparingly used accent action — filtering, non-primary confirms.", - "html": "", - "css": ".ds-btn-secondary { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 44px; padding: 10px 16px; border-radius: 8px; font-weight: 500; font-size: 0.875rem; border: none; cursor: pointer; touch-action: manipulation; background: #DD971A; color: #ffffff; transition: filter 150ms ease-out; } .ds-btn-secondary:hover { filter: brightness(1.1); } .ds-btn-secondary:focus-visible { outline: 2px solid #00723F; outline-offset: 2px; }" + "description": "White-filled, charcoal-outlined — never a second brand color.", + "html": "", + "css": ".ds-btn-secondary { background:#ffffff; color:#383838; }" }, { "name": "Ghost Button", "kind": "button", "refersTo": "button-ghost", - "description": "Low-commitment counterpart to Primary — cancel, clear filters.", - "html": "", - "css": ".ds-btn-ghost { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 44px; padding: 10px 16px; border-radius: 8px; font-weight: 500; font-size: 0.875rem; cursor: pointer; touch-action: manipulation; background: transparent; color: #2D3748; border: 1px solid color-mix(in oklab, #2D3748 15%, transparent); transition: background-color 150ms ease-out; } .ds-btn-ghost:hover { background: color-mix(in oklab, #2D3748 5%, transparent); } .ds-btn-ghost:focus-visible { outline: 2px solid #00723F; outline-offset: 2px; }" + "description": "Low-commitment counterpart — Cancelar/Limpiar.", + "html": "", + "css": ".ds-btn-ghost { display:inline-flex; align-items:center; justify-content:center; gap:8px; min-height:44px; padding:10px 16px; border-radius:2px; border:2px solid rgba(56,56,56,0.35); background:transparent; color:#383838; font-family:'JetBrains Mono Variable',monospace; letter-spacing:0.02em; font-weight:500; } .ds-btn-ghost:hover { background:#f8f8f7; border-color:#383838; }" }, { - "name": "Status Pill", - "kind": "chip", - "refersTo": "badge", - "description": "Tinted-fill status indicator used for every préstamo/material estado; solid-fill only for the confirmed Vencido exception.", - "html": "Disponible Mantenimiento Vencido", - "css": ".ds-pill { display: inline-flex; align-items: center; border-radius: 9999px; padding: 2px 10px; font-size: 0.75rem; font-weight: 600; white-space: nowrap; } .ds-pill-primary { background: color-mix(in oklab, #00723F 13%, transparent); color: #00723F; } .ds-pill-secondary { background: color-mix(in oklab, #DD971A 15%, transparent); color: #c78614; } .ds-pill-danger-solid { background: #C53030; color: #ffffff; }" - }, - { - "name": "Card", + "name": "Raised Card", "kind": "card", "refersTo": "card", - "description": "Flat white container — the system's default surface for content, forms, and KPI tiles. No shadow, hairline ink-mix border only.", - "html": "

Inicia sesión

Accede con tu cuenta institucional.

", - "css": ".ds-card { background: #ffffff; border-radius: 8px; padding: 24px; border: 1px solid color-mix(in oklab, #2D3748 10%, transparent); } .ds-card-title { font-size: 1.125rem; font-weight: 600; margin: 0 0 4px; color: #2D3748; } .ds-card-body { font-size: 0.875rem; opacity: 0.7; margin: 0; color: #2D3748; }" + "description": "Discrete, singular, actionable surface — login card, dashboard tile, KPI tile.", + "html": "

Catálogo

Ver material disponible.

", + "css": ".ds-card-raised { display:block; background:#ffffff; border:2px solid #383838; border-radius:2px; padding:24px; box-shadow:-6px 6px 0 0 #383838; transition:transform 100ms ease-out; } .ds-card-raised:hover, .ds-card-raised:focus-visible { transform:translate(-6px,6px); box-shadow:0 0 0 0 transparent; } .ds-card-raised h2 { font-family:'JetBrains Mono Variable',monospace; text-transform:uppercase; letter-spacing:0.02em; font-size:1.125rem; margin:0 0 4px; color:#383838; } .ds-card-raised p { color:#a1a1a1; font-family:'JetBrains Mono Variable',monospace; font-size:0.875rem; margin:0; }" }, { - "name": "Input Field", + "name": "Flat Card / Table Wrapper", + "kind": "card", + "refersTo": "card", + "description": "Dense, repeated content — tables, list rows. Bordered, no shadow.", + "html": "
Fila de tabla o lista
", + "css": ".ds-card-flat { background:#ffffff; border:1.5px solid #383838; border-radius:2px; padding:24px; font-family:'JetBrains Mono Variable',monospace; color:#383838; }" + }, + { + "name": "Text Input", "kind": "input", "refersTo": "input", - "description": "Labeled text field with hairline border and single-color focus outline (no glow, no border-color shift).", - "html": "
", - "css": ".ds-label { display: block; font-size: 0.875rem; font-weight: 500; margin-bottom: 4px; color: color-mix(in oklab, #2D3748 90%, transparent); } .ds-input { width: 100%; min-height: 44px; border-radius: 8px; padding: 10px 12px; background: #ffffff; border: 1px solid color-mix(in oklab, #2D3748 20%, transparent); font-size: 0.875rem; touch-action: manipulation; transition: border-color 150ms, box-shadow 150ms; } .ds-input:focus-visible { outline: 2px solid #00723F; outline-offset: 2px; }" + "description": "Flat, bordered, no shadow, no glow focus.", + "html": "", + "css": ".ds-input { width:100%; min-height:44px; padding:10px 12px; border-radius:2px; border:2px solid #383838; background:#ffffff; font-family:'JetBrains Mono Variable',monospace; color:#383838; } .ds-input:focus { outline:none; border-color:#6fc2ff; }" }, { - "name": "Mobile Dock Nav Item", + "name": "Status Badge", + "kind": "chip", + "refersTo": "badge", + "description": "Tinted fill, semantic-color border, always charcoal text.", + "html": "Disponible", + "css": ".ds-badge { display:inline-flex; align-items:center; border-radius:2px; padding:2px 8px; font-family:'JetBrains Mono Variable',monospace; font-size:0.75rem; font-weight:600; text-transform:uppercase; letter-spacing:0.03em; color:#383838; } .ds-badge-positive { background:color-mix(in oklab, #38c1b0 18%, white); border:1.5px solid #38c1b0; } .ds-badge-danger { background:color-mix(in oklab, #f38e84 18%, white); border:1.5px solid #f38e84; }" + }, + { + "name": "Nav Item (desktop sidebar)", "kind": "nav", - "refersTo": "nav-dock-item", - "description": "Signature component: icon-only floating pill navigation for the alumno/admin mobile layout, replacing a conventional bottom tab bar.", - "html": "", - "css": ".ds-dock { display: inline-flex; align-items: center; gap: 4px; border-radius: 9999px; padding: 8px; backdrop-filter: blur(8px); background: rgba(255,255,255,0.95); box-shadow: 0 10px 30px -8px rgba(0,0,0,0.25); border: 1px solid color-mix(in oklab, #2D3748 10%, transparent); } .ds-dock-item { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 9999px; color: color-mix(in oklab, #2D3748 70%, transparent); transition: background-color 150ms, color 150ms, transform 150ms; text-decoration: none; } .ds-dock-item:hover { background: color-mix(in oklab, #2D3748 5%, transparent); color: #2D3748; } .ds-dock-item:active { transform: scale(0.95); } .ds-dock-item-active { background: #00723F; color: #ffffff; }" - }, - { - "name": "Toast", - "kind": "custom", - "refersTo": "toast", - "description": "Signature component: floating top-center notification with a colored circular icon chip, survives page reload via a sessionStorage queue.", - "html": "
Solicitud enviadaMultímetro digital · pendiente de aprobación
", - "css": ".ds-toast { display: flex; align-items: flex-start; gap: 12px; border-radius: 16px; padding: 12px 16px; background: rgba(255,255,255,0.85); backdrop-filter: blur(20px); box-shadow: 0 16px 40px -12px rgba(0,0,0,0.25); border: 1px solid color-mix(in oklab, #2D3748 10%, transparent); color: #2D3748; max-width: 26rem; } .ds-toast-icon { flex-shrink: 0; display: grid; place-items: center; width: 36px; height: 36px; border-radius: 9999px; background: #00723F; color: #ffffff; } .ds-toast-title { display: block; font-weight: 600; font-size: 0.875rem; } .ds-toast-desc { display: block; font-size: 0.75rem; opacity: 0.7; margin-top: 2px; }" + "refersTo": "nav-item", + "description": "Uppercase mono label; active = solid Sky Crayon chip with charcoal border.", + "html": "Catálogo", + "css": ".ds-nav-item { display:flex; align-items:center; gap:12px; padding:10px 12px; border-radius:2px; border:2px solid transparent; font-family:'JetBrains Mono Variable',monospace; text-transform:uppercase; letter-spacing:0.02em; font-size:0.875rem; color:#383838; } .ds-nav-item-active { background:#6fc2ff; border-color:#383838; } .ds-nav-item:not(.ds-nav-item-active):hover { background:#f8f8f7; }" } ], "narrative": { - "northStar": "La Bitácora Digital", - "overview": "LabPréstamos is the printed loan sheet and the Excel logbook it used to feed, collapsed into one live surface. Every visual decision defends that lineage: nothing decorative, nothing that would feel out of place next to an official UABC form, but nothing that feels like a cold spreadsheet either. The system is cálido pero confiable — warm but reliable — and it earns the \"warm\" half narrowly: a gold accent, soft rounded corners, and gentle motion sit on top of an otherwise flat, administrative base.\n\nThe page itself stays flat and quiet — white cards on a pale green-grey surface, hairline borders instead of shadows, tabular numbers lined up like a real ledger. Depth is reserved for the two things that are genuinely floating above the page: the toast notifications and the mobile navigation dock. That asymmetry is deliberate — it is how the system tells the eye \"this is the document\" from \"this is the chrome.\"\n\nTwo audiences read this bitácora differently. The alumno's default device is the phone — the catalog, the request modal, the pill-shaped floating dock are all tuned for thumbs. The admin's default device is the desktop — dense tables, a persistent sidebar, KPI tiles. Neither view degrades on the other's device; every screen ships both a table and a card-list rendering.", + "northStar": "The Equipment Tag on a Terminal Readout", + "overview": "LabPréstamos was rebuilt against a fully pinned external brief (MotherDuck's crayon-terminal-on-cream neobrutalism), translated from a marketing site's vocabulary into an operational lab tool's. The identity keeps the source system's mechanics intact — a warm cream canvas, monospace type standing in for a terminal readout, hard 2px corners, and a flat charcoal-offset shadow that never blurs — while dropping what only belonged to marketing (hand-drawn duck illustrations, scrolling marquees, testimonial cards). What replaces the duck is the product's own mark: a loan tag with a punch-hole, echoing the paper ticket this system replaced. Depth here is binary, not a hierarchy: an element either sits flat on the page or it's a discrete raised object that presses into its own shadow on hover. Color plays a stricter role than in the source brief: Sky Crayon blue is reserved for actionable, filled controls; Mint Sketch was pulled from the source's decorative rainbow set and fixed as the positive-status color; Coral Sketch was fixed as the sole danger color, split into a light fill and a separately darkened AA-compliant text variant.", "keyCharacteristics": [ - "Flat administrative base (white cards, hairline ink-tinted borders, no ambient shadow)", - "Shadow appears only on floating overlay chrome — the mobile dock and toasts — never on the page plane", - "Every status is a tinted pill: 12–15% color wash background, full-strength text", - "Tabular numbers everywhere a quantity, date, or stock figure appears", - "One typeface (Inter Variable) carrying every role through weight and size alone", - "Motion is a courtesy, not a performance: motion-safe:-gated, specific-property transitions, no transition-all" + "Cream page canvas, white card surfaces, charcoal (never pure black) as the universal line, border, and text color", + "Every corner is 2px — no pills, no circles, no rounded-full anywhere", + "One shadow, one behavior: -6px 6px 0 0 charcoal, hard-edged, flattens into the element on hover/focus", + "JetBrains Mono Variable everywhere, 0.02em tracking, uppercase on structural labels only", + "Status pills are tinted fills with a matching-color border and always-charcoal text", + "Dense, repeated surfaces stay flat and bordered; only discrete, single-instance elements carry the shadow" ], "rules": [ - { "name": "The Ink-Mix Border Rule", "body": "Every hairline border and divider is color-mix(in oklab, var(--color-tinta) N%, transparent), never a literal gray hex. A border is always \"ink, diluted,\" so it stays coherent with body text at any opacity.", "section": "colors" }, - { "name": "The Vencido Exception", "body": "Every status pill uses the tinted-fill pattern except an overdue loan (\"Vencido\"), which gets a solid peligro fill with white text — on both the table badge and, on mobile, a light danger wash across the entire card background. This is the system's one permitted alarm.", "section": "colors" }, - { "name": "The Tabular Numbers Rule", "body": "Any figure that represents a count — cantidad, stock (disponible/total), KPI values, dates — is rendered with font-variant-numeric: tabular-nums. Numbers must align like a ledger column.", "section": "typography" }, - { "name": "The Table/Card Swap Rule", "body": "Every data-dense admin view ships two complete renderings of the same data: a table for md:block and a card list for md:hidden. This is a structural swap, not a responsive squeeze.", "section": "layout" }, - { "name": "The Flat Page, Floating Chrome Rule", "body": "If an element is part of the document (a card, a table, a form), it stays flat with a hairline border. If an element floats above the document regardless of scroll (the dock, a toast), it gets a soft ambient shadow plus backdrop-blur.", "section": "elevation" } + { "name": "The Charcoal Text Rule", "body": "No filled or tinted surface ever carries white or colored text — text is always Charcoal Ink. Color differentiates status and action through background fill and border only.", "section": "colors" }, + { "name": "The One Shadow Rule", "body": "-6px 6px 0 0 var(--color-ink) is the only shadow value in the system. It never blurs, never softens, never gets a second 'subtle' variant.", "section": "colors" }, + { "name": "The Tabular Numbers Rule", "body": "Any figure representing a count carries font-variant-numeric: tabular-nums.", "section": "typography" }, + { "name": "The Press Rule", "body": "Every raised element's hover state is the same transform: translate exactly the shadow's own offset, so the shadow disappears under the element. No brightness filter, no color darken.", "section": "elevation" }, + { "name": "The Tinted-Fill-Bordered Rule", "body": "Badge background = the semantic color mixed 18-25% into white; border = the semantic color at full strength; text = Charcoal Ink always.", "section": "components" } ], "dos": [ - "Do use color-mix(in oklab, var(--color-tinta) N%, transparent) for any new border or divider — never a literal gray.", - "Do keep shadow exclusive to floating overlay chrome (toasts, the mobile dock).", - "Do use the tinted-pill pattern (12–15% background, full-strength text) for any new status or state indicator.", - "Do apply font-variant-numeric: tabular-nums to any new numeric quantity, stock figure, or date.", - "Do ship both a md:block table and a md:hidden card-list rendering for any new data-dense admin view.", - "Do gate all motion with motion-safe: and animate specific properties, never transition-all.", - "Do give every interactive control a 44px minimum tap target and touch-action: manipulation." + "Do use the exact shadow -6px 6px 0 0 var(--color-ink) for every raised element, and the exact press transform translate(-6px, 6px) on hover/focus.", + "Do keep every radius at 2px — buttons, cards, inputs, dialogs, badges, chips.", + "Do keep text Charcoal Ink on every fill, tint, or border color.", + "Do reserve Sky Crayon for actionable, filled controls; use Positive/Danger/Secondary for status.", + "Do keep dense/repeated surfaces flat and bordered; reserve the hard shadow for discrete, singular surfaces.", + "Do draw every icon as an SVG — never a unicode glyph or emoji.", + "Do apply font-variant-numeric: tabular-nums to every numeric quantity, stock figure, or date.", + "Do ship both a table and a card-list rendering for every data-dense admin view." ], "donts": [ - "Don't add a shadow to .card, .btn, or .input — the system's flatness on the page plane is deliberate.", - "Don't introduce a second typeface. Inter Variable carries every role through weight and size.", - "Don't give a status pill a solid saturated fill — the tinted wash is the rule, and Vencido is the one confirmed exception, not a precedent to extend.", - "Don't reach for rounded-2xl outside the login mark and toasts; it is reserved for \"first impression / interruption\" moments.", - "Don't replace the mobile bottom dock with a hamburger drawer, or the desktop sidebar with a top bar — the device-appropriate nav pattern per breakpoint is deliberate." + "Don't use blur, glass, or a soft ambient shadow anywhere.", + "Don't introduce a radius above 2px, or a pill/circle shape, anywhere.", + "Don't use a second filled brand color for secondary actions.", + "Don't reuse Sky Crayon for status pills.", + "Don't introduce a second typeface.", + "Don't stand a unicode glyph in for an icon." ] } } diff --git a/.impeccable/review/desktop.png b/.impeccable/review/desktop.png new file mode 100644 index 0000000..0e83e05 Binary files /dev/null and b/.impeccable/review/desktop.png differ diff --git a/.impeccable/review/mobile.png b/.impeccable/review/mobile.png new file mode 100644 index 0000000..f516c68 Binary files /dev/null and b/.impeccable/review/mobile.png differ diff --git a/AGENTS.md b/AGENTS.md index 09b579e..497a7d0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -170,3 +170,20 @@ Fases 3, 4/5 y 6 tocan carpetas de rutas distintas (`src/pages/alumno/*`, `src/p - Estado: cambios visuales completos y verificados. Servidor de dev quedó corriendo en background (`astro dev status`/`astro dev logs`/`astro dev stop` para gestionarlo). Pendiente: deploy a producción cuando el usuario lo confirme. - **2026-08-16 — Deploy del rediseño impeccable a producción**. Confirmado por el usuario. `git push` + `ssh buglabs 'cd ~/labre-web && git pull && docker compose up -d --build'`. Container recreado (healthy en ~8s), sin downtime perceptible. Smoke test: `/login` → 200, `/` → 302 (redirige por middleware sin sesión). Cambios ya vivos en https://prestamos.buglabs.dev. + +- **2026-08-16 — Reemplazo total de identidad visual: MotherDuck (`/impeccable`)**. El usuario pegó el `DESIGN.md` completo de MotherDuck ("crayon-coded terminal on cream paper", neobrutalista) y pidió que este proyecto adoptara ese estilo. Se trató como redesign de mundo visual completo (no refinamiento) siguiendo `reference/new-work.md` del skill impeccable: el brief venía 100% fijado (colores, tipografía, sombra, radios, componentes documentados), así que se saltó el proceso de generación de conceptos/dice-roll ("el brief gana" — instrucción explícita del skill cuando hay un mundo visual ya pinneado) y se fue directo a comprometer el mundo, construir, e inspeccionar. Sin generación de imágenes disponible en este entorno → build "code-led" (sin comps). + - Contrato de dirección grabado como comentario HTML al inicio de `` en `Layout.astro` (THESIS/OWN-WORLD/STORY/FIRST VIEWPORT/FORM/FINISH), verificado que sobrevive en el build de producción (`dist/`). + - **Reasignación semántica de la paleta** (la traducción real del trabajo — MotherDuck es un sitio de marketing sin estados de status; este es un panel operativo que sí los necesita): + - Verde institucional → **Sky Crayon `#6fc2ff`**, único color de acción rellena (botones primarios, nav activo). + - Dorado institucional → **Duck Bill Orange `#ff9538`**, acento disperso (antes reservado a "ilustración/marquee" en el brief original, reutilizado aquí porque la app no tiene ninguna de las dos cosas). + - Rojo de error → **Coral Sketch**, mismo problema que el sistema anterior con el verde: el fill claro (`#f38e84`) falla contraste AA como texto, así que se separó en `--color-danger` (fill/borde) y `--color-danger-text` (`#a83224`, oscurecido) — fix aplicado con sed dirigido en los 14 archivos que usaban `color: var(--color-danger)` como texto, verificado que no tocó ningún `border-color`/`background` de paso. + - **Mint Sketch `#38c1b0`** se sacó del set decorativo "rainbow" del brief original y se fijó como color de status "positivo" (disponible/aprobado/devuelto) — el brief de MotherDuck nunca necesitó esto porque no tiene estados de negocio. + - **Regla del texto siempre carbón**: ningún fill o borde de color lleva texto blanco o del mismo color — siempre `--color-ink` (`#383838`). Se auditó y corrigió cada instancia de `color: white`/`text-white` en botones de eliminar/rechazar y el chip activo de filtro de categorías. + - **Radio 2px en todo el sistema**: override de `--radius-lg/2xl/3xl` a `2px` en el `@theme` de Tailwind (cascada automática a casi todo el árbol) + `sed` global de `rounded-full` → `rounded-[2px]` en 8 archivos (dock móvil, toaster, badges de estado). Cero píldoras/círculos sobrevivieron. + - **Sombra dura única** `-6px 6px 0 0 var(--color-ink)` con interacción "press" (`translate(-6px,6px)` en hover, vuelve a su posición en active a mitad de camino) aplicada vía clases compartidas nuevas en `global.css` (`.btn`, `.card-raised`, `.card-link`) — cero cambios por archivo necesarios en la mayoría de botones/links porque ya usaban esas clases compartidas. Los 9 `` nativos (modales) sí se tocaron uno por uno vía `sed` para añadir borde + sombra ya que no usaban una clase compartida. + - **Reskin manual** (no cubierto por cascada de tokens): `AppLayout.astro` (sidebar pasó de panel verde sólido a panel blanco con borde carbón; dock móvil pasó de píldora flotante con blur a barra inferior plana sin sombra, fiel a la regla del brief "nav buttons are flat"), `Toaster.tsx` (quitado `backdrop-blur-xl`/`bg-white/85`/sombra ambiental → borde + sombra dura), `login.astro`/`403.astro`/`404.astro` (headline uppercase, mismo tratamiento de card elevada), favicon (recoloreado carbón-sobre-crema, regenerado `.ico` vía chromium headless + imagemagick). + - **Nuevo `BrandMark`**: mismo glifo de ficha/etiqueta de la sesión anterior, sin cambios de forma — solo de color (antes verde/dorado institucional, ahora carbón-sobre-azul-cielo o wash blanco translúcido sobre panel blanco). + - Verificación: `npm run build` limpio; detector mecánico (`detect.mjs`) sin hallazgos sobre los 27 archivos tocados; ronda de screenshots batched (desktop 1440px + mobile 390px) sobre 11 páginas vía bypass temporal de middleware solo-DEV (mismo patrón de sesiones anteriores, revertido al terminar) — sin defectos materiales, no hizo falta una segunda ronda de fixes. + - **Revisión de cierre y documentación**: sin subagentes `impeccable-finish-reviewer`/`impeccable-documenter` instalados en este entorno, ambos pases corrieron in-thread siguiendo `reference/degraded/finish-reviewer.md` y `reference/degraded/documenter.md` — disclosure explícito de la sustitución. `DESIGN.md` reescrito completo desde el mundo construido (ya no describe "La Bitácora Digital" verde institucional). `PRODUCT.md` → Brand Commitments actualizado con nota de reemplazo fechada y los valores activos. + - **Deliberadamente NO tocado**: arquitectura de layout (sidebar desktop + dock mobile se mantienen, solo cambió su piel), copy/contenido, lógica de negocio, el escudo oficial UABC (sigue sin fabricarse, por instrucción explícita en `PRODUCT.md`). + - Estado: cambios completos y verificados localmente, **sin commitear ni desplegar** — el usuario ya tiene un deploy previo (UABC verde) en producción; este reemplazo de identidad es mucho más grande y espera confirmación explícita antes de `git push` + deploy. diff --git a/DESIGN.md b/DESIGN.md index d8026fd..4482e00 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -1,44 +1,38 @@ --- name: LabPréstamos -description: Sistema institucional UABC para préstamo de material de laboratorio — la bitácora de papel y Excel, digitalizada. +description: Sistema institucional UABC para préstamo de material de laboratorio — un ticket de equipo, cruzado con un terminal de desarrollador. colors: - verde-institucional: "#00723F" - verde-institucional-hover: "#005f34" - dorado-uabc: "#DD971A" - dorado-uabc-hover: "#c78614" - superficie: "#F4F7F5" - tinta: "#2D3748" - peligro: "#C53030" + cream: "#f4efea" + frost: "#ffffff" + chalk: "#f8f8f7" + ice: "#ebf9ff" + ink: "#383838" + pencil: "#a1a1a1" + graphite: "#818181" + primary: "#6fc2ff" + primary-hover: "#56b4f7" + secondary: "#ff9538" + secondary-hover: "#ef8526" + danger: "#f38e84" + danger-hover: "#ec7469" + danger-text: "#a83224" + positive: "#38c1b0" + attention: "#ffde00" typography: - headline: - fontFamily: "Inter Variable, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif" - fontSize: "1.5rem (2xl) → 1.875rem (3xl) md+" - fontWeight: 600 - lineHeight: 1.25 - title: - fontFamily: "Inter Variable, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif" - fontSize: "1.125rem" - fontWeight: 600 - lineHeight: 1.4 body: - fontFamily: "Inter Variable, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif" + fontFamily: "JetBrains Mono Variable, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace" fontSize: "0.875rem" fontWeight: 400 lineHeight: 1.5 + letterSpacing: "0.02em" label: - fontFamily: "Inter Variable, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif" - fontSize: "0.875rem" - fontWeight: 500 - lineHeight: 1.3 - caption: - fontFamily: "Inter Variable, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif" + fontFamily: "JetBrains Mono Variable, ui-monospace, monospace" fontSize: "0.75rem" - fontWeight: 400 - lineHeight: 1.4 + fontWeight: 500 + letterSpacing: "0.05em" + textTransform: uppercase rounded: - control: "0.5rem" - emphasis: "1rem" - pill: "9999px" + control: "2px" spacing: tight: "0.5rem" base: "1rem" @@ -46,28 +40,28 @@ spacing: section: "2rem" components: button-primary: - backgroundColor: "{colors.verde-institucional}" - textColor: "#ffffff" + backgroundColor: "{colors.primary}" + textColor: "{colors.ink}" rounded: "{rounded.control}" padding: "10px 16px" button-secondary: - backgroundColor: "{colors.dorado-uabc}" - textColor: "#ffffff" + backgroundColor: "{colors.frost}" + textColor: "{colors.ink}" rounded: "{rounded.control}" padding: "10px 16px" button-ghost: backgroundColor: "transparent" - textColor: "{colors.tinta}" + textColor: "{colors.ink}" rounded: "{rounded.control}" padding: "10px 16px" card: - backgroundColor: "#ffffff" - textColor: "{colors.tinta}" + backgroundColor: "{colors.frost}" + textColor: "{colors.ink}" rounded: "{rounded.control}" padding: "24px" input: - backgroundColor: "#ffffff" - textColor: "{colors.tinta}" + backgroundColor: "{colors.frost}" + textColor: "{colors.ink}" rounded: "{rounded.control}" padding: "10px 12px" --- @@ -76,151 +70,140 @@ components: ## Overview -**Creative North Star: "La Bitácora Digital"** +**Creative North Star: "The Equipment Tag on a Terminal Readout"** -LabPréstamos is the printed loan sheet and the Excel logbook it used to feed, collapsed into one live surface. Every visual decision defends that lineage: nothing decorative, nothing that would feel out of place next to an official UABC form, but nothing that feels like a cold spreadsheet either. The system is **cálido pero confiable** — warm but reliable — and it earns the "warm" half narrowly: a gold accent, soft rounded corners, and gentle motion sit on top of an otherwise flat, administrative base. +LabPréstamos was rebuilt against a fully pinned external brief (MotherDuck's crayon-terminal-on-cream neobrutalism), translated from a marketing site's vocabulary into an operational lab tool's. The identity keeps the source system's mechanics intact — a warm cream canvas, monospace type standing in for a terminal readout, hard 2px corners, and a flat charcoal-offset shadow that never blurs — while dropping what only belonged to marketing (hand-drawn duck illustrations, scrolling marquees, testimonial cards). What replaces the duck is the product's own mark: a loan tag with a punch-hole, echoing the paper ticket this system replaced. -The page itself stays flat and quiet — white cards on a pale green-grey surface, hairline borders instead of shadows, tabular numbers lined up like a real ledger. Depth is reserved for the two things that are genuinely *floating above* the page: the toast notifications and the mobile navigation dock. That asymmetry is deliberate — it is how the system tells the eye "this is the document" from "this is the chrome." +Depth here is binary, not a hierarchy: an element either sits flat on the page (bordered, no shadow — tables, list rows, inputs, the nav) or it's a discrete raised object that "presses" into its own shadow on hover (buttons, standalone cards, modals, the login card). There is no soft, no blur, no ambient glow anywhere in the system — every visual weight decision reads as a hard yes/no. -Two audiences read this bitácora differently. The alumno's default device is the phone — the catalog, the request modal, the pill-shaped floating dock are all tuned for thumbs. The admin's default device is the desktop — dense tables, a persistent sidebar, KPI tiles. Neither view degrades on the other's device; every screen ships both a table and a card-list rendering. +Color plays a stricter role here than in the source brief. Sky Crayon blue is reserved for actionable, filled controls — never for status. A teal (Mint Sketch) was pulled out of the source's decorative rainbow set and fixed as the system's "positive/available" status color, because an operational tool needs that distinction and a marketing site never did. Coral Sketch was likewise fixed as the sole danger color, with a separately darkened text variant (`--color-danger-text`, `#a83224`) for body copy, because the source coral fails AA contrast on white at normal text weight — the fill stays light and loud, the text goes dark and readable. **Key Characteristics:** -- Flat administrative base (white cards, hairline ink-tinted borders, no ambient shadow) -- Shadow appears only on floating overlay chrome — the mobile dock and toasts — never on the page plane -- Every status is a tinted pill: 12–15% color wash background, full-strength text -- Tabular numbers everywhere a quantity, date, or stock figure appears -- One typeface (Inter Variable) carrying every role through weight and size alone -- Motion is a courtesy, not a performance: `motion-safe:`-gated, specific-property transitions, no `transition-all` +- Cream page canvas, white card surfaces, charcoal (`#383838`, never pure black) as the universal line, border, and text color +- Every corner is 2px — buttons, cards, inputs, dialogs, badges, the brand mark's own chip. No pills, no circles, no `rounded-full` anywhere in the system +- One shadow, one behavior: `-6px 6px 0 0 var(--color-ink)`, hard-edged, zero blur, on every button, standalone card, toast, and modal — and it flattens into the element on hover/focus (`translate(-6px, 6px)`) +- JetBrains Mono Variable everywhere, 0.02em tracking, uppercase treatment on nav labels, button labels, section labels, and headlines +- Status pills and count badges are tinted fills with a matching-color 1.5px border and always-charcoal text — color differentiates via background and border, never via colored type +- Dense, repeated surfaces (tables, list-item cards, form inputs) stay flat and bordered; only discrete, single-instance elements carry the shadow ## Colors -Two institutional hues carry the whole palette under a strict 60/30/10 split — green dominant, gold as a deliberately rare accent, everything else neutral ink and white. +The palette is inherited near-verbatim from the pinned MotherDuck brief; only the semantic *assignment* changes to fit an operational tool instead of a marketing site. ### Primary -- **Verde Institucional** (`#00723F`): the UABC green. Primary buttons, the desktop sidebar's entire background, active nav states, focus rings, "healthy/positive" status pills (disponible, aprobado, devuelto), the login mark. This is the color of the system's authority — it appears on anything that confirms, approves, or orients. +- **Sky Crayon** (`#6fc2ff`): the system's only filled-action color. Primary buttons, active nav state, focused/selected chips, "Solicitar"/"Aprobar"/"Filtrar"/"Continuar con Google." Text on it is always Charcoal Ink, never white — matching every other filled control in the system. ### Secondary -- **Dorado UABC** (`#DD971A`): the institutional gold. Used sparingly and only as an accent — secondary buttons, the "en préstamo" KPI tile, "mantenimiento" status pills, the logo-mark square on the sidebar/mobile header. Never the dominant color of a screen. +- **Duck Bill Orange** (`#ff9538`): sparse accent, repurposed from the source's "illustration/marquee" role (this app has neither) into the operational tool's low-emphasis-but-real accent — "Exportar CSV," "Mantenimiento" status tint, the "En préstamo" KPI tone. ### Neutral -- **Superficie** (`#F4F7F5`): the page background — a near-white green-grey, not pure white. Everything else (cards, dialogs, the desktop sidebar's text, dock) sits on top of it. -- **Tinta** (`#2D3748`): body text, and — critically — the **source color for every border in the system**. Borders are never a fixed gray; they're `color-mix(in oklab, var(--color-ink) N%, transparent)` at 8–20% depending on prominence. This ties every hairline to the same ink token instead of a separate gray scale. -- **Peligro** (`#C53030`): errors, destructive actions, and the one status that is allowed to break the tinted-pill convention (see Named Rules). +- **Cream Paper** (`#f4efea`): the page canvas — every screen's base layer. +- **Frost White** (`#ffffff`): card, dialog, sidebar, and nav surfaces — the raised layer on cream. +- **Chalk Gray** (`#f8f8f7`): hover wash on flat (non-shadowed) interactive rows, and the neutral badge fill for "Baja"/inactive states. +- **Charcoal Ink** (`#383838`): the universal line — every border, every default text color, the shadow color itself. Never pure black. +- **Pencil Gray** (`#a1a1a1`): secondary/muted text (labels under headings, metadata, roles) — a real token, not an opacity trick. + +### Status colors (extension beyond the source brief) +- **Mint Sketch → Positive** (`#38c1b0`): "disponible," "aprobado," "devuelto en préstamo" status. Pulled out of the source's decorative rainbow-button palette because the operational tool needs a status color distinct from the primary action color; the source brief, built for a marketing site, never had this problem. +- **Coral Sketch → Danger** (`#f38e84` fill / `#a83224` text): "rechazado," "vencido," destructive actions. Split into two values because the fill color (used for badge backgrounds, borders, button fills) is deliberately light and loud, while body text needs the darker `-text` variant to clear 4.5:1 contrast on white. ### Named Rules -**The Ink-Mix Border Rule.** Every hairline border and divider is `color-mix(in oklab, var(--color-tinta) N%, transparent)`, never a literal gray hex. A border is always "ink, diluted," so it stays coherent with body text at any opacity. +**The Charcoal Text Rule.** No filled or tinted surface ever carries white or colored text — text is always Charcoal Ink, full stop. Color differentiates status and action through background fill and border only. This is a deliberate tightening of the source brief, which used white nav-button text as an exception; LabPréstamos has no exception. -**The Vencido Exception.** Every status pill uses the tinted-fill pattern (see Components → Badges) *except* an overdue loan ("Vencido"), which gets a solid `peligro` fill with white text — on both the table badge and, on mobile, a light danger wash across the entire card background. This is the system's one permitted alarm: reserve a solid semantic fill for the single state that genuinely needs to interrupt a scan, and only that state. +**The One Shadow Rule.** `-6px 6px 0 0 var(--color-ink)` is the only shadow value in the system. It never blurs, never softens, never gets a second variant for "subtle" elevation — an element either has this exact shadow or it has none. ## Typography -**Body & Display Font:** Inter Variable (with `ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif` fallback) +**Body & Display Font:** JetBrains Mono Variable (weights 100–800 available; the system uses 400/500/600), with `ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace` fallback. Chosen as the pinned brief's own named substitute for the licensed Aeonik Mono. -**Character:** One typeface for the entire system — hierarchy comes from weight and size, never a second family. It reads as a single, consistent institutional voice rather than a "brand font + UI font" split. +**Character:** One monospace face for the entire system, 0.02em letter-spacing throughout to open up the mechanical density of mono at small sizes. Uppercase tracking is reserved for structural labels (nav items, button text, form labels, section headers, the login headline) — body copy, names, and descriptions stay in normal case so real data (student names, material names) stays legible rather than shouting. ### Hierarchy -- **Headline** (600, 1.5rem → 1.875rem at `md:`, 1.25 line-height): page-level `

`s — "Mis préstamos", "Estadísticas", the login screen's title. -- **Title** (600, 1.125rem, 1.4 line-height): section and card headers — "Inicia sesión", "Inventario", empty-state headings. -- **Body** (400, 0.875rem, 1.5 line-height): the default text size for the whole system — table cells, descriptions, list content. Notably small-scale throughout; this is a dense operational tool, not editorial content. -- **Label** (500, 0.875rem): form field labels (`.label`), rendered at 90% ink opacity. -- **Caption** (400, 0.75rem, opacity 60–80%): metadata — matrícula, inventory numbers, timestamps, hint text under fields. +- **Headline** (600, 1.5rem → 1.875rem at `md:`, uppercase): page `

`s, the login screen's title. +- **Title** (600, 1.125rem): section and card headers, dialog titles — not uppercased by default, uppercased where it doubles as a nav/label element. +- **Body** (400, 0.875rem, 1.5 line-height): the default text size — table cells, descriptions, list content. +- **Label** (500, 0.75rem, 0.05em tracking, uppercase): every form field label (`.label`), nav items, badge text. +- **Caption** (400, 0.75rem, Pencil Gray): metadata — matrícula, inventory numbers, timestamps. ### Named Rules -**The Tabular Numbers Rule.** Any figure that represents a count — cantidad, stock (`disponible / total`), KPI values, dates — is rendered with `font-variant-numeric: tabular-nums`. Numbers must align like a ledger column, because that is literally what they replace. +**The Tabular Numbers Rule.** Any figure representing a count — cantidad, stock, KPI values, dates — carries `font-variant-numeric: tabular-nums`, inherited unchanged from the prior system. JetBrains Mono's own distinctive dotted-zero glyph reinforces the "terminal readout" reading at large KPI sizes. ## Layout -The system runs on Tailwind's default breakpoint (`md:` = 768px) as the single hard split between the mobile and desktop layouts — there is no intermediate tablet treatment. - -**Desktop (≥768px):** a fixed 256px (`w-64`) left sidebar in solid Verde Institucional, full page height. It is collapsible: a hamburger button above the content toggles a `sidebar-collapsed` class on ``, animated with a 200ms `translateX(-100%)`; the choice persists in `localStorage` and is applied via a blocking inline `