refactor(assets): simplify breakpoints and update UnoCSS CLI config

- Refactor SCSS breakpoints to flat min-width values (680/960/1200/1440)
  with _bp-max helper deriving ranges from adjacent breakpoints
- Add 'xs': null to breakpoints map, remove special-case handling
- Update responsive image sizes to match exact breakpoint max values
- Add print:hidden to UnoCSS safelist as replacement for d-none-print
- Move UnoCSS CLI config to cli.entry in uno.config.ts
- Rename css:build → unocss, css:dev → unocss:watch
- Update documentation (CLAUDE.md, CONTRIBUTING.md, copilot-instructions.md)
This commit is contained in:
Cell
2026-06-27 16:28:31 +08:00
parent 279021d591
commit fb0c00e274
10 changed files with 73 additions and 63 deletions
+2 -2
View File
@@ -22,10 +22,10 @@ This document defines the detailed coding standards for the FixIt theme project.
- Use UnoCSS atomic classes for common utilities (`hidden`, `me-1`, `text-center`, etc.)
- Theme colors: `bg-primary`, `text-success`, `text-danger`, etc. (mapped via `@theme` in `uno.config.ts`)
- Responsive: `sm:` (>=681px), `md:` (>=961px), `lg:` (>=1201px), `xl:` (>=1441px). Use `max-sm:` for xs (<681px)
- Responsive: `sm:` (>=680px), `md:` (>=960px), `lg:` (>=1200px), `xl:` (>=1440px). Use `max-sm:` for xs (<680px)
- Wrap SVG elements with `<!-- @unocss-skip-start -->` / `<!-- @unocss-skip-end -->` to avoid false positives from `d` attribute values
- Dynamically generated classes (e.g., `order-*`) must be added to `safelist` in `uno.config.ts`
- Run `pnpm css:build` after modifying `uno.config.ts` or templates that use UnoCSS classes
- Run `pnpm unocss` after modifying `uno.config.ts` or templates that use UnoCSS classes
## TypeScript Coding Standards
+3 -3
View File
@@ -90,7 +90,7 @@ Cross-module communication uses the shared `eventBus` singleton, not direct modu
Hugo Pipes processes all assets. The key orchestration is in `_partials/base/assets.html`:
- **CSS**: `scss/main.scss` is the entry point importing `core/`, `pages/`, `widgets/`, `custom`. SCSS variables are configured via `hugo:vars` (user-configurable from `[params.appearance]`) and `hugo:vars/internal` (theme system config). UnoCSS utility classes are pre-built to `assets/css/unocss.css` (via `pnpm css:build`), loaded before the main SCSS bundle.
- **CSS**: `scss/main.scss` is the entry point importing `core/`, `pages/`, `widgets/`, `custom`. SCSS variables are configured via `hugo:vars` (user-configurable from `[params.appearance]`) and `hugo:vars/internal` (theme system config). UnoCSS utility classes are pre-built to `assets/css/unocss.css` (via `pnpm unocss`), loaded before the main SCSS bundle.
- **JS**: `_partials/function/js-build.html` wraps `js.Build` with minify-in-production defaults. Hugo's `@params` injection passes config values into TypeScript at build time.
- **Third-party libraries**: Stored in `assets/lib/` (vendored, not npm-managed). Tracked by `librarybot.yml` and updated weekly by the `hugo-fixit/librarybot` GitHub Action. Can be overridden via CDN config in `assets/data/cdn/jsdelivr.yml` or `unpkg.yml`.
@@ -112,10 +112,10 @@ Hugo Pipes processes all assets. The key orchestration is in `_partials/base/ass
### UnoCSS (`assets/css/unocss.css`)
- Pre-built utility classes generated by `pnpm css:build` (config: `uno.config.ts`)
- Pre-built utility classes generated by `pnpm unocss` (config: `uno.config.ts`)
- Use atomic classes for common utilities (`hidden`, `me-1`, `text-center`, etc.)
- Theme colors mapped via `@theme`: `bg-primary`, `text-success`, `text-danger`, etc.
- Responsive breakpoints: `sm:` (>=681px), `md:` (>=961px), `lg:` (>=1201px), `xl:` (>=1441px). Use `max-sm:` for xs (<681px)
- Responsive breakpoints: `sm:` (>=680px), `md:` (>=960px), `lg:` (>=1200px), `xl:` (>=1440px). Use `max-sm:` for xs (<680px)
- Wrap SVG elements with `<!-- @unocss-skip-start -->` / `<!-- @unocss-skip-end -->` to avoid false positives from `d` attribute values
- `safelist` in `uno.config.ts` for dynamically generated classes (e.g., `order-*`, `sm:hidden`)
+2 -2
View File
@@ -82,9 +82,9 @@ pnpm gen:lexers
pnpm preview
# Build UnoCSS utility classes (pre-built, committed to repo)
pnpm css:build
pnpm unocss
# Watch mode for UnoCSS (for theme developers)
pnpm css:dev
pnpm unocss:watch
```
> [!TIP]
+5 -2
View File
@@ -82,9 +82,12 @@
.opacity-50{opacity:0.5;}
.shadow{--un-shadow:var(--un-shadow-inset) 0 1px 3px 0 var(--un-shadow-color, rgb(0 0 0 / 0.1)),var(--un-shadow-inset) 0 1px 2px -1px var(--un-shadow-color, rgb(0 0 0 / 0.1));box-shadow:var(--un-ring-offset-shadow), var(--un-ring-shadow), var(--un-shadow);}
.blur{--un-blur:blur(8px);filter:var(--un-blur) var(--un-brightness) var(--un-contrast) var(--un-drop-shadow) var(--un-grayscale) var(--un-hue-rotate) var(--un-invert) var(--un-saturate) var(--un-sepia);}
@media (max-width: 680.9px){
@media print{
.print\:hidden{display:none;}
}
@media (max-width: 679.9px){
.max-sm\:hidden{display:none;}
}
@media (min-width: 681px){
@media (min-width: 680px){
.sm\:hidden{display:none;}
}
+41 -44
View File
@@ -1,45 +1,48 @@
// Responsive breakpoints matching the project's media query design
// ┌──────────────────────────────────────────────────────┐
// │ xs │ sm │ md │ lg │ xl
// │ ≤680 │ 681960 │ 9611200 │ 12011440 │ ≥1441 │
// └──────────────────────────────────────────────────────┘
// FixIt Responsive breakpoints system design
// ┌──────────────────────────────────────────┐
// │ Name │ min-width │ max-width │ Direction
// ├──────┼───────────┼───────────┼──────────────┤
// │ xs │ (null) │ 679.9px │ only │
// │ sm │ 680px │ 959.9px │ only/up/down │
// │ md │ 960px │ 1199.9px │ only/down │
// │ lg │ 1200px │ 1439.9px │ only/up │
// │ xl │ 1440px │ (null) │ only │
// └──────┴───────────┴───────────┴──────────────┘
@use "sass:map";
@use "sass:list";
$breakpoints: (
'xs': (
'min': null,
'max': 680.9px,
),
'sm': (
'min': 681px,
'max': 960.9px,
),
'md': (
'min': 961px,
'max': 1200.9px,
),
'lg': (
'min': 1201px,
'max': 1440.9px,
),
'xl': (
'min': 1441px,
'max': null,
),
'xs': null,
'sm': 680px,
'md': 960px,
'lg': 1200px,
'xl': 1440px,
) !default;
/// Get the max-width for a breakpoint (0.1px below the next breakpoint).
@function _bp-max($name) {
// Ordered list of breakpoint names for range derivation
$keys: map.keys($breakpoints);
$idx: list.index($keys, $name);
@if $idx == null or $idx >= list.length($keys) {
@return null;
}
$next: list.nth($keys, $idx + 1);
@return map.get($breakpoints, $next) - 0.1px;
}
/// Apply styles within a named media target.
/// @param {String} $target - Target name: xs | sm | md | lg | xl | print | reduce-motion
/// @param {String} $direction - Range direction: only | up | down [default: only]
///
/// @example
/// // xs: 680px
/// // xs: < 680px
/// @include media('xs') { ... }
/// // sm: 681px 960px
/// // sm: 680px 959.9px
/// @include media('sm') { ... }
/// // md and above: ≥ 961px
/// // md and above: ≥ 960px
/// @include media('md', 'up') { ... }
/// // lg and below: ≤ 1440px
/// // lg and below: ≤ 1199.9px
/// @include media('lg', 'down') { ... }
/// // print media
/// @include media('print') { ... }
@@ -54,21 +57,13 @@ $breakpoints: (
@media screen and (prefers-reduced-motion: reduce) {
@content;
}
} @else {
$range: map.get($breakpoints, $target);
$min: null;
$max: null;
} @else if map.has-key($breakpoints, $target) {
$min: map.get($breakpoints, $target);
$max: _bp-max($target);
@if $range != null {
$min: map.get($range, 'min');
$max: map.get($range, 'max');
}
@if $range == null {
@warn "Unknown media target `#{$target}`. Expected: xs, sm, md, lg, xl, print, reduce-motion.";
} @else if $direction == 'only' {
@if $direction == 'only' {
@if $min == null {
// xs: max-width only, no lower bound
// xs: max-width only
@media only screen and (max-width: $max) {
@content;
}
@@ -84,7 +79,7 @@ $breakpoints: (
}
} @else if $direction == 'up' {
@if $min == null {
// xs-up: no lower bound, always applies
// xs-up: always applies
@content;
} @else {
@media only screen and (min-width: $min) {
@@ -93,7 +88,7 @@ $breakpoints: (
}
} @else if $direction == 'down' {
@if $max == null {
// xl-down: no upper bound, always applies
// xl-down: always applies
@content;
} @else {
@media only screen and (max-width: $max) {
@@ -103,5 +98,7 @@ $breakpoints: (
} @else {
@warn "Unknown direction `#{$direction}`. Expected: only, up, down.";
}
} @else {
@warn "Unknown media target `#{$target}`. Expected: xs, sm, md, lg, xl, print, reduce-motion.";
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
{{- hugo.Store.Set "version" "v1.0.0-mqt0ysyp" -}}
{{- hugo.Store.Set "version" "v1.0.0-mqw3lkj1" -}}
{{- .Store.Set "this" dict -}}
{{- partial "init/detection-env.html" . -}}
+1 -1
View File
@@ -151,7 +151,7 @@
(dict "Process" "resize 1200x webp" "descriptor" "1200w")
(dict "Process" "resize 1600x webp" "descriptor" "1600w")
-}}
{{- dict "Src" $image "Title" $.Description "Resources" .Resources "Matches" $matches "Loading" "eager" "Sizes" "(max-width: 680px) 100vw, (max-width: 960px) 80vw, (max-width: 1440px) 56vw, 800px" "OptimConfig" $optim | partial "plugin/image.html" -}}
{{- dict "Src" $image "Title" $.Description "Resources" .Resources "Matches" $matches "Loading" "eager" "Sizes" "(max-width: 679.9px) 100vw, (max-width: 959.9px) 80vw, (max-width: 1439.9px) 56vw, 800px" "OptimConfig" $optim | partial "plugin/image.html" -}}
</div>
{{- end -}}
+1 -1
View File
@@ -18,7 +18,7 @@
(dict "Process" "fill 1200x504 Center webp" "descriptor" "1200w")
(dict "Process" "fill 1600x672 Center webp" "descriptor" "1600w")
-}}
{{- dict "Src" $image "Title" $.Title "Resources" $.Resources "Matches" $matches "Loading" "eager" "Width" "800" "Height" "336" "Sizes" "(max-width: 680px) 100vw, (max-width: 960px) 80vw, (max-width: 1440px) 56vw, 800px" "OptimConfig" $optim "Alt" (printf "Featured image for %v" $.Title) | partial "plugin/image.html" -}}
{{- dict "Src" $image "Title" $.Title "Resources" $.Resources "Matches" $matches "Loading" "eager" "Width" "800" "Height" "336" "Sizes" "(max-width: 679.9px) 100vw, (max-width: 959.9px) 80vw, (max-width: 1439.9px) 56vw, 800px" "OptimConfig" $optim "Alt" (printf "Featured image for %v" $.Title) | partial "plugin/image.html" -}}
</a>
</div>
{{- end -}}
+2 -2
View File
@@ -42,8 +42,8 @@
"version": "pnpm -F versioning start prod",
"changelog": "auto-changelog-plus --starting-date 2021-12-18",
"gen:lexers": "pnpm -F chroma-lexers start",
"css:build": "unocss \"layouts/**/*.html\" \"assets/js/**/*.ts\" -o assets/css/unocss.css",
"css:dev": "pnpm css:build --watch",
"unocss": "unocss",
"unocss:watch": "unocss --watch",
"prepare": "simple-git-hooks"
},
"devDependencies": {
+15 -5
View File
@@ -17,12 +17,12 @@ export default defineConfig({
],
theme: {
// FixIt responsive breakpoints (min-width, mobile-first)
// xs (max-width: 680.9px) use max-sm: prefix in UnoCSS
// xs (< 680px) use max-sm: prefix in UnoCSS
breakpoints: {
sm: '681px',
md: '961px',
lg: '1201px',
xl: '1441px',
sm: '680px',
md: '960px',
lg: '1200px',
xl: '1440px',
},
colors: {
primary: 'var(--fi-primary)',
@@ -65,6 +65,7 @@ export default defineConfig({
// Responsive visibility
'sm:hidden',
'max-sm:hidden',
'print:hidden',
// Print page breaks
'break-before-page',
'break-after-page',
@@ -76,4 +77,13 @@ export default defineConfig({
'assets/js/**/*.ts',
],
},
// CLI entry for unocss / unocss:watch
cli: {
entry: [
{
patterns: ['layouts/**/*.html', 'assets/js/**/*.ts'],
outFile: 'assets/css/unocss.css',
},
],
},
})