From aa9df5c0dad104d16014fd35fe142082a8aba446 Mon Sep 17 00:00:00 2001 From: DanMat Date: Sat, 5 Sep 2026 22:59:31 -0400 Subject: [PATCH] Slice 8b: RAS public theme (guest menu) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The guest-facing side of the rebuild — the public menu, in the RAS identity. - theme/ (source co-located in the app repo; deploy drops it into the site's themes/): a dark, committed restaurant look uplifted from the original RAS #222, with a warm gold accent and an elegant printed-menu feel. - Templates: layout / header (RAS wordmark) / footer / entry / 404, and collection-menu_items — the menu grouped by its category relation (expanded by EntryView) with prices and dotted leaders. theme.json declares it + nav. - assets/app.css — one stylesheet, responsive, no build step. - Theme CI: php -l the templates on 8.2 + 8.3 (view files, no test DB). Reads the same menu_items collection the Orders picker uses; no core change. Templates lint clean locally; rendered a standalone preview to verify the look. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/theme-ci.yml | 27 ++++++ theme/assets/app.css | 107 ++++++++++++++++++++++ theme/templates/404.php | 15 +++ theme/templates/collection-menu_items.php | 57 ++++++++++++ theme/templates/entry.php | 17 ++++ theme/templates/footer.php | 17 ++++ theme/templates/header.php | 20 ++++ theme/templates/layout.php | 44 +++++++++ theme/theme.json | 16 ++++ 9 files changed, 320 insertions(+) create mode 100644 .github/workflows/theme-ci.yml create mode 100644 theme/assets/app.css create mode 100644 theme/templates/404.php create mode 100644 theme/templates/collection-menu_items.php create mode 100644 theme/templates/entry.php create mode 100644 theme/templates/footer.php create mode 100644 theme/templates/header.php create mode 100644 theme/templates/layout.php create mode 100644 theme/theme.json diff --git a/.github/workflows/theme-ci.yml b/.github/workflows/theme-ci.yml new file mode 100644 index 0000000..670bd29 --- /dev/null +++ b/.github/workflows/theme-ci.yml @@ -0,0 +1,27 @@ +name: Theme CI + +# The RAS public theme is plain PHP templates (no build step, no test DB), so CI +# just lints them for syntax on the supported PHP versions. + +on: + push: + branches: [nimbus-rebuild, 'slice/**'] + paths: ['theme/**', '.github/workflows/theme-ci.yml'] + pull_request: + paths: ['theme/**', '.github/workflows/theme-ci.yml'] + +jobs: + lint: + runs-on: ubuntu-latest + strategy: + matrix: + php: ['8.2', '8.3'] + steps: + - uses: actions/checkout@v4 + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: none + - name: Lint templates + run: | + for f in theme/templates/*.php; do php -l "$f"; done diff --git a/theme/assets/app.css b/theme/assets/app.css new file mode 100644 index 0000000..67f00f2 --- /dev/null +++ b/theme/assets/app.css @@ -0,0 +1,107 @@ +/* + * RAS — the public face of the Restaurant Automation System. + * A single, committed dark restaurant look (uplifted from the original #222), + * with a warm gold accent and an elegant printed-menu feel. + */ + +:root { + --ground: #1a1c20; + --surface: #23262c; + --text: #ecedee; + --muted: #9aa0a8; + --gold: #d4a017; + --gold-bright: #e7b62f; + --rule: rgba(255, 255, 255, 0.09); + --serif: Georgia, "Iowan Old Style", "Times New Roman", serif; + --sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; +} + +* { box-sizing: border-box; } + +html { -webkit-text-size-adjust: 100%; } + +body { + margin: 0; + background: var(--ground); + color: var(--text); + font-family: var(--sans); + line-height: 1.6; + font-size: 17px; +} + +a { color: var(--gold); text-decoration: none; } +a:hover { color: var(--gold-bright); } + +img { max-width: 100%; height: auto; } + +.wrap { width: 100%; max-width: 900px; margin: 0 auto; padding: 0 1.25rem; } +.measure { max-width: 40rem; } + +.skip-link { + position: absolute; left: -999px; top: 0; + background: var(--gold); color: #1a1c20; padding: .6rem 1rem; border-radius: 6px; +} +.skip-link:focus { left: 1rem; top: 1rem; z-index: 10; } + +/* Header */ +.site-header { + position: sticky; top: 0; z-index: 5; + background: rgba(20, 22, 26, 0.92); + backdrop-filter: saturate(140%) blur(6px); + border-bottom: 1px solid var(--rule); +} +.header-inner { display: flex; align-items: center; justify-content: space-between; min-height: 64px; gap: 1rem; } +.brand { display: inline-flex; align-items: center; gap: .7rem; color: var(--text); } +.brand:hover { color: var(--text); } +.brand-mark { + font-weight: 800; letter-spacing: .12em; font-size: .82rem; + background: var(--gold); color: #1a1c20; + padding: .3rem .5rem; border-radius: 5px; +} +.brand-name { font-family: var(--serif); font-size: 1.15rem; letter-spacing: .01em; } +.site-nav a { font-size: .95rem; letter-spacing: .04em; text-transform: uppercase; } + +/* Menu page */ +.menu-page { padding: 3rem 0 4rem; } +.menu-head { text-align: center; margin-bottom: 2.75rem; } +.eyebrow { + margin: 0 0 .6rem; font-size: .72rem; font-weight: 700; + letter-spacing: .22em; text-transform: uppercase; color: var(--gold); +} +.menu-head h1 { font-family: var(--serif); font-size: 2.6rem; font-weight: 400; margin: 0 0 .5rem; } +.lede { color: var(--muted); margin: 0; } + +.menu-group { margin: 0 0 2.75rem; } +.menu-group h2 { + font-family: var(--serif); font-weight: 400; font-size: 1.5rem; + margin: 0 0 1.1rem; padding-bottom: .5rem; border-bottom: 1px solid var(--rule); + color: var(--text); +} +.menu-list { list-style: none; margin: 0; padding: 0; } +.menu-item { padding: .7rem 0; border-bottom: 1px dashed rgba(255, 255, 255, 0.05); } +.menu-item:last-child { border-bottom: 0; } +.menu-row { display: flex; align-items: baseline; gap: .35rem; } +.menu-name { font-weight: 500; } +.menu-leader { flex: 1; border-bottom: 1px dotted rgba(255, 255, 255, 0.28); transform: translateY(-.25rem); } +.menu-price { font-variant-numeric: tabular-nums; color: var(--gold); font-weight: 600; white-space: nowrap; } +.menu-desc { margin: .3rem 0 0; color: var(--muted); font-size: .92rem; max-width: 46ch; } +.empty { color: var(--muted); text-align: center; padding: 2rem 0; } + +/* Generic page + 404 */ +.page { padding: 3rem 0 4rem; } +.page h1 { font-family: var(--serif); font-weight: 400; font-size: 2.2rem; margin: 0 0 1rem; } +.prose { color: var(--text); } +.notfound { text-align: center; } + +/* Footer */ +.site-footer { border-top: 1px solid var(--rule); padding: 2.5rem 0; margin-top: 2rem; } +.footer-inner { text-align: center; } +.footer-name { font-family: var(--serif); font-size: 1.15rem; margin: 0 0 .3rem; } +.footer-meta { color: var(--muted); margin: 0 0 .6rem; } +.footer-fine { color: var(--muted); font-size: .8rem; margin: 0; opacity: .8; } + +@media (max-width: 40rem) { + .menu-head h1 { font-size: 2.1rem; } + .brand-name { display: none; } + body { font-size: 16px; } +} diff --git a/theme/templates/404.php b/theme/templates/404.php new file mode 100644 index 0000000..23621d5 --- /dev/null +++ b/theme/templates/404.php @@ -0,0 +1,15 @@ + +
+
+

RAS

+

Not on the menu

+

That page isn’t here. Have a look at the menu instead.

+
+
diff --git a/theme/templates/collection-menu_items.php b/theme/templates/collection-menu_items.php new file mode 100644 index 0000000..53d32e4 --- /dev/null +++ b/theme/templates/collection-menu_items.php @@ -0,0 +1,57 @@ +> $entries + * @var callable $e + */ +$groups = []; +foreach ($entries as $item) { + $fields = is_array($item['fields'] ?? null) ? $item['fields'] : []; + $rel = $fields['category'] ?? null; + $cat = is_array($rel) && isset($rel[0]['title']) && (string) $rel[0]['title'] !== '' + ? (string) $rel[0]['title'] + : 'More'; + $groups[$cat][] = $item; +} +?> + diff --git a/theme/templates/entry.php b/theme/templates/entry.php new file mode 100644 index 0000000..943588b --- /dev/null +++ b/theme/templates/entry.php @@ -0,0 +1,17 @@ + $entry + * @var callable $e + */ +$fields = is_array($entry['fields'] ?? null) ? $entry['fields'] : []; +$body = trim((string) ($fields['body'] ?? '')); +?> +
+
+

+
+
+
diff --git a/theme/templates/footer.php b/theme/templates/footer.php new file mode 100644 index 0000000..f313681 --- /dev/null +++ b/theme/templates/footer.php @@ -0,0 +1,17 @@ + +
+ +
diff --git a/theme/templates/header.php b/theme/templates/header.php new file mode 100644 index 0000000..0c33a1b --- /dev/null +++ b/theme/templates/header.php @@ -0,0 +1,20 @@ + + diff --git a/theme/templates/layout.php b/theme/templates/layout.php new file mode 100644 index 0000000..3bcae66 --- /dev/null +++ b/theme/templates/layout.php @@ -0,0 +1,44 @@ + $meta + * @var string $head extra HTML contributed by plugins (already-rendered, trusted) + */ +$pageTitle = isset($title) && $title !== '' ? $title . ' · ' . $appName : $appName; +$meta = $meta ?? []; +$cssVer = substr((string) @hash_file('crc32b', __DIR__ . '/../assets/app.css'), 0, 8); +?> + + + + + + <?= $e($pageTitle) ?> + + + + + + + + + + + + + + + +
+ +
+ + + diff --git a/theme/theme.json b/theme/theme.json new file mode 100644 index 0000000..d754a80 --- /dev/null +++ b/theme/theme.json @@ -0,0 +1,16 @@ +{ + "name": "RAS", + "version": "0.1.0", + "description": "The public face of the Restaurant Automation System — a dark, elegant guest menu carrying the RAS identity. Plain PHP templates, one stylesheet, no build step.", + "nav": ["menu_items"], + "templates": { + "layout": "HTML shell; includes header and footer.", + "header": "Site header with the RAS wordmark and the menu link.", + "footer": "Footer with hours and contact.", + "collection-menu_items": "The guest menu (the `menu_items` collection), grouped by category with prices.", + "entry": "A single info page.", + "404": "Themed not-found page." + }, + "specialization": "collection-menu_items renders the `menu_items` collection provisioned by the restaurant app — each item has a `price` number and a `category` relation (expanded). Grouped by category name, priced, with dotted leaders.", + "assets": "Files under assets/ are served at /theme/assets/ (e.g. assets/app.css -> /theme/assets/app.css)." +}