一键导入
minoo-frontend-ssr
Use when working on Minoo templates, CSS design system, or SSR rendering in templates/, public/css/, or src/Controller/ render methods
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when working on Minoo templates, CSS design system, or SSR rendering in templates/, public/css/, or src/Controller/ render methods
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Use when working on Minoo HTTP controllers, routing, or request handling in src/Http/Controller/ (or src/Http/Middleware/) or route definitions in src/Provider/
Use when working on Minoo entity types, access policies, service providers, or seed data in src/ or tests/Minoo/
Use when working on Minoo ingestion pipeline, mappers, materializer, or ingest logs in src/Ingestion/ or tests relating to ingestion
Use when working on Minoo search, autocomplete, or NorthCloud search integration in src/Search/ or SearchServiceProvider
| name | minoo:frontend-ssr |
| description | Use when working on Minoo templates, CSS design system, or SSR rendering in templates/, public/css/, or src/Controller/ render methods |
Files: templates/, public/css/*.css, SSR rendering in src/Controller/
Tests: Playwright E2E in e2e/
templates/
├── layouts/base.html.twig # Page shell (only layout; extended by all pages)
├── pages/<domain>/<view>.html.twig # Route-level templates
│ └── static/ # Static content pages (about, how-it-works, etc.)
├── components/
│ ├── shared/{layout,ui,data,feedback}/ # Cross-domain partials
│ └── domain/<name>/ # Domain-scoped partials
├── email/ # Mail bodies rendered by framework AuthMailer
├── 403.html.twig # Framework hardcodes path — do not move
└── 404.html.twig # Framework hardcodes path — do not move
Framework constraint: vendor/waaseyaa/ssr/src/RenderController.php hardcodes 403.html.twig and 404.html.twig at the templates root. Do not relocate.
No path-based template auto-resolution. Controllers call $this->twig->render('pages/<domain>/<view>.html.twig', ...) explicitly. Each route in App\Provider\AppServiceProvider maps to a controller method that chooses its template path.
Static pages with no domain logic are served through StaticPageController, which renders templates from pages/static/ or the appropriate pages/<domain>/ folder.
All page templates extend layouts/base.html.twig and override blocks:
{% extends "layouts/base.html.twig" %}
{% block title %}Page Title{% endblock %}
{% block content %}
{# Page content here #}
{% endblock %}
Available blocks: title, meta_description, og_title, og_description, og_image, og_type, head, content, scripts.
Components live under templates/components/:
shared/layout/ — sidebar-nav, location-bar, breadcrumb, hero-imageshared/ui/ — chat, language-switcher, pagination, theme-toggle, user-role-cardshared/data/ — search-result-card, empty-state, protocol-noticeshared/feedback/ — flash-messagesdomain/<name>/ — cards, filters, and domain-scoped partials (events/, teachings/, groups/, people/, businesses/, communities/, feed/, language/, oral-histories/)Include components with explicit with and only:
{% include "components/domain/events/card.html.twig" with {
title: event.get('title'),
type: event.get('type'),
date: event.get('starts_at'),
url: "/events/" ~ event.get('slug'),
} only %}
public/css/minoo.css is a manifest — no rules, only the layer declaration and @import statements.
public/css/
├── minoo.css # Manifest: @layer order + @imports
├── reset.css # @layer reset
├── tokens.css # @layer tokens
├── base.css # @layer base
├── layout.css # @layer layout
├── components.css # @layer components
├── utilities.css # @layer utilities
└── orphans.css # Un-layered rules (font-face, legacy rules, @media print)
Manifest content:
@layer reset, tokens, base, layout, components, utilities;
@import "reset.css";
@import "tokens.css";
@import "base.css";
@import "layout.css";
@import "components.css";
@import "utilities.css";
@import "orphans.css";
orphans.css: Rules that existed outside any @layer block in the pre-split history — @font-face declarations, a few legacy unlayered rule sets, and @media print blocks. Imported after the layered files so they retain highest cascade priority (same behavior as before the split). Do not move rules in or out without understanding the cascade implications.
| Layer | Purpose |
|---|---|
reset | Box-sizing, margin reset, media elements, font inheritance |
tokens | Design tokens: colors, typography, spacing, widths, radii, shadows |
base | Body, headings, links, code, lists — use tokens only |
layout | Page shell: header, sidebar, grid surfaces, app shell |
components | Cards, detail views, filters, pagination, domain UI blocks |
utilities | .flow, .flow-lg, .sr-only, single-purpose helpers |
OKLCH earth / forest / water / sun / berry palette with shadcn-style Tier-2 semantic aliases (--background, --foreground, --muted, --card, --popover, --destructive, --ring, --input). Typography uses fluid clamp() scales; spacing uses a 1.5-ratio fluid scale; widths: --width-prose: 65ch, --width-content: 80rem, --width-narrow: 40rem, --width-card: 25rem.
margin-block, padding-inline) — no left/rightgap for sibling spacing!importantlayouts/base.html.twig references the manifest with a version query: <link rel="stylesheet" href="/css/minoo.css?v=N">. Bump N after any CSS change.
Cards: .card with container-type: inline-size. Variants: .card--event, .card--group, .card--community, .card--teaching, .card--language, .card--person, .card--elder, .card--detail, .card--dashboard. Parts: .card__badge, .card__title, .card__meta, .card__body, .card__tags. Grid: .card-grid.
Forms: .form > .form__field > .form__label + .form__input. Error state: .form__input--error + .form__error[role="alert"]. Buttons: .form__submit--primary, --secondary, --danger, --sm.
Buttons: .btn with --primary, --secondary, --accent, --ghost, --lg.
Layout: .flow / .flow-lg for vertical rhythm, .prose for readable text, .content-section for page sections.
Detail pages: .detail > .detail__back, .detail__header, .detail__meta, .detail__body.
Entity field access via .get():
{{ item.get('title') }}
{% if item.get('status') == 1 %}Published{% endif %}
Active nav via aria-current:
<a href="/events"{% if path starts with '/events' %} aria-current="page"{% endif %}>
entity.get('field'), not entity.fieldleft/right — always use logical properties (margin-inline-start, padding-block-end)margin for spacing — use gap in flex/grid; .flow pattern for vertical stacks@container); media queries only for page shellpath variable — every SSR controller must pass path in LayoutTwigContext::withAccount(...)minoo.css directly — it's a manifest; add rules to the matching layer file (components.css, layout.css, etc.)orphans.css — unlayered rules outrank all @layer rules; understand the cascade impact before touching?v=N bump — stale CSS after deploy is the #1 "it looks broken" causeAppServiceProvider — later registration silently wins. Games use games.<name> for /games/<name> and games.<name>.short for /<name>docs/specs/frontend-ssr.md — authoritative directory layout, inheritance, CSS architecture, smoke test routeswaaseyaa_get_spec api-layer — Response types, RenderController