Skip to content

Commit fe10fb2

Browse files
hHolyMollyclaude
andcommitted
feat: breakpoint system, design tokens, picture partial, inline critical CSS, DX fixes (v3.1.0)
Styles core: - _mixins.scss: $breakpoints map + media-up/down/between, hover (any-hover), fluid() clamp helper - vars.scss: semantic colors, z-index scale, --radius, --header-height; applied across modals/button/anchors - normalize: [hidden] hardening, anchor offset below fixed header, body bg; dropped hanging-punctuation, global overscroll-behavior, no-op ::selection - utils: .visually-hidden alias, removed WP .screen-reader-text idiom and duplicate .icon rule Bug fixes: - modals.js: modal-to-modal switching (isLocked early-return) - headerScroll.js: sentinel-based observer (works for fixed/sticky headers) - demo.html: fallbackCopy out-of-scope variables - watch: shared partials outside layouts/components now rebuild pages - dev server: real bound port logged (BrowserSync silently shifts on busy port) - Ctrl+C on pnpm dev exits 0 instead of 1 Features: - components/_picture.html + demo.jpg (WebP/AVIF usage example) - critical CSS actually works: compiled and inlined into <head> when enabled - pnpm zip (dated handoff archive), pnpm serve (no-rebuild preview) - clean:demo asks for confirmation Tooling: - browserslist modernized (83 -> 36 targets), caniuse-lite refreshed - lint:check gates warnings, format:check added (both in CI) - package.json: private, packageManager, engines.pnpm; eslint/stylelint cache - .gitattributes (LF + binaries), .editorconfig cleanup, VS Code extensions - deploy.yml: workflow_dispatch, keep 404.html on Pages - .template/paths.js pruned; narrowed Google Fonts to used weights Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 0febc4b commit fe10fb2

38 files changed

Lines changed: 398 additions & 183 deletions

‎.browserslistrc‎

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1-
# Browsers that we support
2-
# https://browsersl.ist/
3-
4-
# Production: Modern browsers with good support
5-
last 2 versions
6-
> 1%
7-
not dead
8-
not op_mini all
9-
10-
# Safari needs special attention
11-
Safari >= 14
12-
iOS >= 14
1+
last 2 chrome versions
2+
last 2 edge versions
3+
last 2 firefox versions
4+
Firefox ESR
5+
last 2 safari major versions
6+
last 2 ios major versions
7+
last 2 and_chr versions
8+
last 2 samsung versions

‎.editorconfig‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ indent_style = space
99
indent_size = 2
1010
insert_final_newline = true
1111
trim_trailing_whitespace = true
12+
max_line_length = 120
1213

1314
[*.md]
1415
trim_trailing_whitespace = false
@@ -21,6 +22,3 @@ indent_size = 2
2122

2223
[*.{scss,css}]
2324
indent_size = 2
24-
25-
[Makefile]
26-
indent_style = tab

‎.gitattributes‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Normalize line endings — LF everywhere (matches .editorconfig / Prettier)
2+
* text=auto eol=lf
3+
4+
# Binary files
5+
*.ico binary
6+
*.png binary
7+
*.jpg binary
8+
*.jpeg binary
9+
*.gif binary
10+
*.webp binary
11+
*.avif binary
12+
*.woff binary
13+
*.woff2 binary
14+
*.ttf binary
15+
*.otf binary
16+
*.mp4 binary
17+
*.webm binary
18+
*.zip binary

‎.github/workflows/deploy.yml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ name: Deploy to GitHub Pages
33
on:
44
push:
55
branches: [main]
6+
workflow_dispatch:
67

78
concurrency:
89
group: pages
@@ -23,17 +24,16 @@ jobs:
2324
with:
2425
persist-credentials: false
2526
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
26-
with:
27-
version: 10
2827
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
2928
with:
3029
node-version: 22
3130
cache: pnpm
3231
- run: pnpm install --frozen-lockfile
3332
- run: pnpm lint:check
33+
- run: pnpm format:check
3434
- run: pnpm build:prod
3535
- name: Remove template pages
36-
run: rm -f dist/template.html dist/404.html
36+
run: rm -f dist/template.html
3737
- uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5
3838
- uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3
3939
with:

‎.template/paths.js‎

Lines changed: 5 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,12 @@
11
/**
22
* Template Paths
3-
* Utilities for setup scripts in .template/setup/
3+
* Shared helper for setup scripts in .template/setup/
44
*/
55

6-
import path from 'path';
7-
import { fileURLToPath } from 'url';
6+
import path from 'node:path';
7+
import { fileURLToPath } from 'node:url';
88

9-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
10-
const rootDir = path.resolve(__dirname, '../');
9+
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../');
1110

12-
// Directory names (mirrors gulp/configs/paths.js)
13-
const folders = {
14-
src: 'src',
15-
styles: 'styles',
16-
scripts: 'scripts',
17-
html: 'html',
18-
layouts: 'layouts',
19-
pages: 'pages',
20-
components: 'components',
21-
};
22-
23-
// ─────────────────────────────────────────────────────────────
24-
// Path Helpers
25-
// ─────────────────────────────────────────────────────────────
26-
27-
/**
28-
* Path relative to project root
29-
*/
11+
/** Path relative to the project root */
3012
export const resolvePath = (...parts) => path.join(rootDir, ...parts);
31-
32-
/**
33-
* Path to source files
34-
*/
35-
export const srcPath = (...parts) => resolvePath(folders.src, ...parts);
36-
37-
/**
38-
* Path to gulp tasks
39-
*/
40-
export const gulpPath = (...parts) => resolvePath('gulp', ...parts);
41-
42-
// ─────────────────────────────────────────────────────────────
43-
// Export
44-
// ─────────────────────────────────────────────────────────────
45-
46-
export const templatePaths = {
47-
root: rootDir,
48-
template: __dirname,
49-
50-
// Computed paths
51-
src: srcPath(),
52-
srcStyles: srcPath(folders.styles),
53-
srcScripts: srcPath(folders.scripts),
54-
srcHtml: srcPath(folders.html),
55-
srcHtmlLayouts: srcPath(folders.html, folders.layouts),
56-
srcHtmlPages: srcPath(folders.html, folders.pages),
57-
srcHtmlComponents: srcPath(folders.html, folders.components),
58-
59-
// Gulp
60-
gulpTasks: gulpPath('tasks'),
61-
gulpConfigs: gulpPath('configs'),
62-
gulpUtils: gulpPath('utils'),
63-
64-
// Helpers
65-
resolvePath,
66-
srcPath,
67-
gulpPath,
68-
};

‎.template/setup/clean-demo.js‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
import fs from 'node:fs';
9+
import readline from 'node:readline';
910
import { resolvePath } from '../paths.js';
1011

1112
const DEMO_COMPONENT = 'src/html/components/demo.html';
@@ -49,7 +50,29 @@ function log(message, type = 'success') {
4950
console.log(` ${icons[type]} ${message}`);
5051
}
5152

53+
function confirm(question) {
54+
return new Promise((resolve) => {
55+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
56+
57+
rl.question(` ${question} (y/N): `, (answer) => {
58+
rl.close();
59+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
60+
});
61+
});
62+
}
63+
5264
console.log('\n 🧹 Demo cleanup\n');
65+
console.log(` This will DELETE ${DEMO_COMPONENT}`);
66+
console.log(` and OVERWRITE ${INDEX_PAGE} with a clean skeleton.\n`);
67+
68+
const confirmed = await confirm('Do you want to continue?');
69+
70+
if (!confirmed) {
71+
console.log('\n ❌ Cancelled.\n');
72+
process.exit(0);
73+
}
74+
75+
console.log('');
5376

5477
if (fs.existsSync(resolvePath(DEMO_COMPONENT))) {
5578
fs.rmSync(resolvePath(DEMO_COMPONENT));

‎.template/setup/tailwind/setup.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ async function setup() {
8686

8787
try {
8888
log(`Installing ${config.packages.join(', ')}...`);
89-
execSync(`pnpm add -D ${config.packages.join(' ')}`, { stdio: 'pipe' });
89+
execSync(`pnpm add -D ${config.packages.join(' ')}`, { stdio: 'inherit' });
9090
log('Packages installed.', 'success');
9191
} catch (error) {
9292
log(`Install failed: ${error.message}`, 'error');

‎.template/setup/zip.js‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* Handoff Archive Script
3+
* Run: pnpm zip (builds prod first)
4+
*
5+
* Archives dist/ into <project-name>-<YYYY-MM-DD>.zip at the project root.
6+
* No dependencies: system `zip` on macOS/Linux, Compress-Archive on Windows.
7+
*/
8+
9+
import { execFileSync } from 'node:child_process';
10+
import { existsSync, rmSync, statSync, readFileSync } from 'node:fs';
11+
import { resolvePath } from '../paths.js';
12+
13+
const dist = resolvePath('dist');
14+
15+
if (!existsSync(dist)) {
16+
console.error('\n ✗ dist/ not found — run pnpm build:prod first\n');
17+
process.exit(1);
18+
}
19+
20+
const pkg = JSON.parse(readFileSync(resolvePath('package.json'), 'utf8'));
21+
const date = new Date().toISOString().slice(0, 10);
22+
const archiveName = `${pkg.name}-${date}.zip`;
23+
const archivePath = resolvePath(archiveName);
24+
25+
rmSync(archivePath, { force: true });
26+
27+
try {
28+
if (process.platform === 'win32') {
29+
execFileSync(
30+
'powershell',
31+
['-NoProfile', '-Command', `Compress-Archive -Path dist\\* -DestinationPath '${archivePath}' -Force`],
32+
{ stdio: 'inherit' }
33+
);
34+
} else {
35+
// cwd: dist — archive contents land at the zip root (no dist/ prefix)
36+
execFileSync('zip', ['-r', '-q', archivePath, '.'], { cwd: dist, stdio: 'inherit' });
37+
}
38+
} catch (error) {
39+
console.error(`\n ✗ Archiving failed: ${error.message}\n`);
40+
process.exit(1);
41+
}
42+
43+
const sizeKb = Math.round(statSync(archivePath).size / 1024);
44+
console.log(`\n ✓ ${archiveName} (${sizeKb} KB)\n`);

‎.vscode/extensions.json‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
"dbaeumer.vscode-eslint",
44
"stylelint.vscode-stylelint",
55
"esbenp.prettier-vscode",
6+
"bradlc.vscode-tailwindcss",
67
"formulahendry.auto-rename-tag",
78
"streetsidesoftware.code-spell-checker",
89
"streetsidesoftware.code-spell-checker-russian",
910
"csstools.postcss",
10-
"editorconfig.editorconfig"
11+
"editorconfig.editorconfig",
12+
"mikestead.dotenv"
1113
]
1214
}

‎.vscode/settings.json‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"editor.formatOnSave": true,
55

66
// ── ESLint ─────────────────────────────────────────────────
7-
"eslint.useFlatConfig": true,
87
"editor.codeActionsOnSave": {
98
"source.fixAll.eslint": "explicit",
109
"source.fixAll.stylelint": "explicit"
@@ -16,6 +15,9 @@
1615
"scss.validate": false,
1716

1817
// ── File Associations ──────────────────────────────────────
18+
"files.associations": {
19+
".env.*": "dotenv"
20+
},
1921

2022
// ── Files ──────────────────────────────────────────────────
2123
"files.eol": "\n",

0 commit comments

Comments
 (0)