forked from fa/breadcrumb-the-shire
css: consolidate core stylesheet entrypoint and add contract check
This commit is contained in:
@@ -200,11 +200,13 @@ Zentrale Partials:
|
||||
|
||||
## Asset- und Frontend-System
|
||||
|
||||
- CSS-Layer-Entrypoint: `/web/css/app-layers.css`
|
||||
- Core-CSS-Entrypoint: `/web/css/core.css`
|
||||
- CSS-Layer/Vendor-Basis: `/web/css/app-layers.css`
|
||||
- Vendor-Overrides: `/web/css/vendor-overrides`
|
||||
- Asset-Gruppen: `/config/assets.php`
|
||||
- Template-Styles: `renderTemplateStyles('default'|'login')`
|
||||
- Seiten-Styles per Action: `Buffer::set('style_groups', ...)`
|
||||
- CSS-Contract-Check: `bin/css-contract-check.sh`
|
||||
- JS-Bootstrap:
|
||||
- `/web/js/app-boot.js` (frueh, ohne Module)
|
||||
- `/web/js/app-init.js` (Standardseiten)
|
||||
|
||||
95
bin/css-contract-check.sh
Executable file
95
bin/css-contract-check.sh
Executable file
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
repo_root="$(cd -- "${script_dir}/.." && pwd)"
|
||||
cd "${repo_root}"
|
||||
|
||||
if ! command -v rg >/dev/null 2>&1; then
|
||||
echo "[ERROR] rg is required for css-contract-check." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
failures=0
|
||||
|
||||
ok() {
|
||||
echo "[OK] $*"
|
||||
}
|
||||
|
||||
fail() {
|
||||
echo "[FAIL] $*" >&2
|
||||
failures=$((failures + 1))
|
||||
}
|
||||
|
||||
expected_layer_order='@layer tokens, vendor, layout, vendor-overrides, components, pages, utilities;'
|
||||
if rg -Fq "${expected_layer_order}" web/css/app-layers.css; then
|
||||
ok "Layer order declaration found in web/css/app-layers.css"
|
||||
else
|
||||
fail "Layer order declaration is missing or changed in web/css/app-layers.css"
|
||||
fi
|
||||
|
||||
while IFS= read -r file; do
|
||||
expected_layer=''
|
||||
case "${file}" in
|
||||
web/css/base/*|modules/*/web/css/base/*)
|
||||
expected_layer='tokens'
|
||||
;;
|
||||
web/css/layout/*|modules/*/web/css/layout/*)
|
||||
expected_layer='layout'
|
||||
;;
|
||||
web/css/components/*|modules/*/web/css/components/*)
|
||||
expected_layer='components'
|
||||
;;
|
||||
web/css/pages/*|modules/*/web/css/pages/*)
|
||||
expected_layer='pages'
|
||||
;;
|
||||
web/css/vendor-overrides/*|modules/*/web/css/vendor-overrides/*)
|
||||
expected_layer='vendor-overrides'
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ -z "${expected_layer}" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "${file}" == "web/css/pages/app-error.css" || "${file}" == "web/css/pages/app-error-debug.css" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
detected_layer="$(sed -n '1,120p' "${file}" | sed -nE 's/.*@layer[[:space:]]+([^[:space:]{]+).*/\1/p' | head -n1)"
|
||||
if [[ -z "${detected_layer}" ]]; then
|
||||
fail "${file}: missing @layer declaration near file top"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "${detected_layer}" != "${expected_layer}" ]]; then
|
||||
fail "${file}: @layer ${detected_layer} does not match expected ${expected_layer}"
|
||||
fi
|
||||
done < <(rg --files web/css modules | rg '\.css$')
|
||||
|
||||
remote_import_pattern='@import[[:space:]]+url\(["'"'"']?(https?:)?//|@import[[:space:]]+["'"'"']?(https?:)?//'
|
||||
remote_hits="$(rg -n -g '*.css' -e "${remote_import_pattern}" web/css modules || true)"
|
||||
if [[ -n "${remote_hits}" ]]; then
|
||||
fail "Remote CSS import detected (must vendor locally):"
|
||||
echo "${remote_hits}" >&2
|
||||
else
|
||||
ok "No remote CSS imports detected"
|
||||
fi
|
||||
|
||||
if [[ ! -f web/css/core.css ]]; then
|
||||
fail "web/css/core.css is missing"
|
||||
else
|
||||
first_import="$(rg -n '^@import' web/css/core.css | head -n1 || true)"
|
||||
if [[ "${first_import}" == *'app-layers.css'* ]]; then
|
||||
ok "web/css/core.css imports app-layers.css first"
|
||||
else
|
||||
fail "web/css/core.css must import app-layers.css as first @import"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${failures}" -gt 0 ]]; then
|
||||
echo "[FAIL] CSS contract check failed with ${failures} issue(s)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[OK] CSS contract check passed."
|
||||
@@ -41,6 +41,9 @@ run_step "QG-008 Docs link integrity" \
|
||||
run_step "QG-008 Docs drift contract" \
|
||||
bin/docs-drift-check.sh
|
||||
|
||||
run_step "CSS contract check" \
|
||||
bin/css-contract-check.sh
|
||||
|
||||
run_step "QG-009 Codex skills sync" \
|
||||
bin/codex-skills-sync.sh --check
|
||||
|
||||
|
||||
@@ -19,37 +19,9 @@ return [
|
||||
'css/components/app-brand.css',
|
||||
'css/components/app-file-upload.css',
|
||||
],
|
||||
'default' => [
|
||||
'css/vendor-overrides/multi-select.css',
|
||||
'css/components/app-icon-button.css',
|
||||
'css/components/app-buttons.css',
|
||||
'css/vendor-overrides/gridjs.css',
|
||||
'css/layout/app-topbar.css',
|
||||
'css/layout/app-sidebar.css',
|
||||
'css/components/app-search.css',
|
||||
'css/components/app-list-titlebar.css',
|
||||
'css/components/app-details-titlebar.css',
|
||||
'core' => [
|
||||
'css/core.css',
|
||||
'css/components/app-details-validation-summary.css',
|
||||
'css/components/app-dashboard-titlebar.css',
|
||||
'css/components/app-empty-state.css',
|
||||
'css/components/app-details.css',
|
||||
'css/components/app-details-card.css',
|
||||
'css/components/app-form-info-tile.css',
|
||||
'css/components/app-list-table.css',
|
||||
'css/components/app-list-tabs.css',
|
||||
'css/components/app-tabs.css',
|
||||
'css/components/app-list-toolbar.css',
|
||||
'css/components/app-filter-drawer.css',
|
||||
'css/components/app-active-filter-chips.css',
|
||||
'css/components/app-confirm-dialog.css',
|
||||
'css/components/app-breadcrumb.css',
|
||||
'css/components/app-tile.css',
|
||||
'css/components/app-tooltips.css',
|
||||
'css/components/app-lookup-field.css',
|
||||
'css/components/app-page-editor.css',
|
||||
'css/vendor-overrides/editorjs.css',
|
||||
'css/components/app-page-copy.css',
|
||||
'css/layout/app-aside-icon-bar.css',
|
||||
],
|
||||
'login' => [
|
||||
'css/pages/app-login.css',
|
||||
@@ -65,7 +37,7 @@ return [
|
||||
],
|
||||
],
|
||||
'templates' => [
|
||||
'default' => ['base', 'shared', 'default'],
|
||||
'default' => ['base', 'core'],
|
||||
'login' => ['base', 'shared', 'login'],
|
||||
'error' => ['base', 'error'],
|
||||
'page' => ['base', 'shared', 'page-layout'],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Frontend CSS
|
||||
|
||||
Letzte Aktualisierung: 2026-03-19
|
||||
Letzte Aktualisierung: 2026-04-20
|
||||
|
||||
## Ziel
|
||||
|
||||
@@ -8,8 +8,10 @@ Diese Seite beschreibt die kompakte Standardstruktur für CSS in diesem Projekt:
|
||||
|
||||
## Struktur
|
||||
|
||||
- `/web/css/core.css`
|
||||
- zentraler Core-Entrypoint für die Default-Admin-Oberfläche.
|
||||
- `/web/css/app-layers.css`
|
||||
- zentraler Entrypoint für Layer-Reihenfolge.
|
||||
- zentrale Layer-Reihenfolge + Vendor-Basis.
|
||||
- `/web/css/base`
|
||||
- Variablen, Reset, Typografie, globale Tokens.
|
||||
- `/web/css/layout`
|
||||
@@ -50,6 +52,11 @@ Kurzregel:
|
||||
Asset-Gruppen werden in `/config/assets.php` registriert.
|
||||
Seiten laden bei Bedarf Gruppen über `Buffer::set('style_groups', ...)`.
|
||||
|
||||
Default-Template:
|
||||
|
||||
- `base` lädt Layer-/Token-Basis.
|
||||
- `core` lädt den konsolidierten Core-Stack über `/web/css/core.css`.
|
||||
|
||||
Beispiel (Action):
|
||||
|
||||
```php
|
||||
@@ -76,6 +83,7 @@ Regel: Vendor-Overrides nur für Bibliotheks-Selektoren; projektspezifische UI-S
|
||||
3. Wird die Gruppe auf der Zielseite wirklich geladen (`style_groups`)?
|
||||
4. Gibt es ungewollte Seiteneffekte auf anderen Seiten?
|
||||
5. Sind bestehende Variablen/Konventionen eingehalten?
|
||||
6. Contract-Check lokal ausführen: `bin/css-contract-check.sh`
|
||||
|
||||
## Module-CSS Token-Regel
|
||||
|
||||
|
||||
48
web/css/core.css
Normal file
48
web/css/core.css
Normal file
@@ -0,0 +1,48 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
/*
|
||||
* Core CSS framework entrypoint for the default admin shell.
|
||||
* Keep imports in deterministic order; file-level @layer declarations define precedence.
|
||||
*/
|
||||
@import url("app-layers.css");
|
||||
|
||||
/* Shared foundation used by multiple templates */
|
||||
@import url("components/app-badges.css");
|
||||
@import url("layout/app-shell.css");
|
||||
@import url("components/app-dialog.css");
|
||||
@import url("components/app-blockquote.css");
|
||||
@import url("components/app-forms.css");
|
||||
@import url("components/app-flash.css");
|
||||
@import url("components/app-brand.css");
|
||||
@import url("components/app-file-upload.css");
|
||||
|
||||
/* Default admin experience */
|
||||
@import url("vendor-overrides/multi-select.css");
|
||||
@import url("components/app-icon-button.css");
|
||||
@import url("components/app-buttons.css");
|
||||
@import url("vendor-overrides/gridjs.css");
|
||||
@import url("layout/app-topbar.css");
|
||||
@import url("layout/app-sidebar.css");
|
||||
@import url("components/app-search.css");
|
||||
@import url("components/app-list-titlebar.css");
|
||||
@import url("components/app-details-titlebar.css");
|
||||
@import url("components/app-dashboard-titlebar.css");
|
||||
@import url("components/app-empty-state.css");
|
||||
@import url("components/app-details.css");
|
||||
@import url("components/app-details-card.css");
|
||||
@import url("components/app-form-info-tile.css");
|
||||
@import url("components/app-list-table.css");
|
||||
@import url("components/app-list-tabs.css");
|
||||
@import url("components/app-tabs.css");
|
||||
@import url("components/app-list-toolbar.css");
|
||||
@import url("components/app-filter-drawer.css");
|
||||
@import url("components/app-active-filter-chips.css");
|
||||
@import url("components/app-confirm-dialog.css");
|
||||
@import url("components/app-breadcrumb.css");
|
||||
@import url("components/app-tile.css");
|
||||
@import url("components/app-tooltips.css");
|
||||
@import url("components/app-lookup-field.css");
|
||||
@import url("components/app-page-editor.css");
|
||||
@import url("vendor-overrides/editorjs.css");
|
||||
@import url("components/app-page-copy.css");
|
||||
@import url("layout/app-aside-icon-bar.css");
|
||||
@@ -1,4 +1,4 @@
|
||||
@layer components {
|
||||
@layer pages {
|
||||
/* ─── Layout ─────────────────────────────────────────────────── */
|
||||
|
||||
.app-docs-container {
|
||||
|
||||
Reference in New Issue
Block a user