diff --git a/.cache/pages.json b/.cache/pages.json index f52647700..5f4910f3c 100644 --- a/.cache/pages.json +++ b/.cache/pages.json @@ -25,18 +25,25 @@ "privacy": ["my-data"] }, { - "services": ["create-custom-font-sets", "overview"] + "services": ["consulting", "overview", "web-development"] }, { "tags": [ "apiDesign", + "aws", + "ci", "cms", "code", "crm", + "databaseNormalization", + "deployment", + "devPortals", "graphql", "online-learning", "react", + "restful", "services", + "sqlOptimization", "typescript" ] } diff --git a/.github/instructions/general.instructions.md b/.github/instructions/general.instructions.md index 105a95ef6..d7a904c8c 100644 --- a/.github/instructions/general.instructions.md +++ b/.github/instructions/general.instructions.md @@ -19,6 +19,7 @@ applyTo: "**" - Always create TypeScript files, not JavaScript files. - Prefer destructured imports over namespace imports when importing specific functions from modules (e.g., `import { resolve } from 'path'` instead of `import * as path from 'path'`). - Do not access nanostore observables (e.g., `$consent`) directly from components; expose helper/action methods in `@components/scripts/store` and import those instead. +- Preact exists only for the Markdown E2E harness under `src/lib/markdown/__tests__/e2e`; all production UI components must use Lit web components instead of Preact. # Code Organization and Directory Structure diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7fbc7acc8..27de7da03 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -79,11 +79,9 @@ jobs: - name: Install dependencies run: npm ci --legacy-peer-deps - - name: Sync Astro types - run: npm run sync - - - name: Run Astro check - run: npm run check + # Astro build runs sync, check, and integrations including verify links + - name: Verify green build + run: npm run build - name: Run unit tests (Vitest — coverage with GitHub Actions reporter) id: vitest diff --git a/.husky/pre-commit b/.husky/pre-commit index 09d4fa44b..3bfd6617f 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -18,6 +18,9 @@ fi # Run lint suite (includes TypeScript check + ESLint + Stylelint + JSON formatting) npm run lint +# Make sure types are sync'ed before commit +npm run sync + # Verify branch naming convention .husky/scripts/check-branch-name.sh diff --git a/.vscode/settings.json b/.vscode/settings.json index 85a9a09d0..7af45fa55 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -31,11 +31,14 @@ "Fedibird", "Fediverse", "finnadie", + "firko", "Flink", "FNAME", "fosstodon", "FOUC", + "fprintf", "glidejs", + "gomodule", "GSAP", "hocho", "Hudi", @@ -76,7 +79,12 @@ "pleroma", "Poslovski", "Postg", + "postgrest", + "psql", + "qpart", + "qparts", "Qualys", + "redigo", "registrator", "repost", "reposts", @@ -103,11 +111,13 @@ "tsdoc", "tseslint", "tshirt", + "Turso", "tydata", "unspaced", "unstub", "uppy", "uuidv", + "valyala", "Veeam", "vercel", "virt", diff --git a/_TODO.md b/_TODO.md index 203f11f57..25a24b4fe 100644 --- a/_TODO.md +++ b/_TODO.md @@ -1,6 +1,16 @@ # TODO +## Fix E2E mock container test runner, migrate DB providers + +Refactor from using Suprabase and Upstash to using Astro DB + Turso + +[Astro DB](https://docs.astro.build/en/guides/astro-db/) + +## Mobile Social Shares + +See the example image in Social Shares. + ## Performance Implement mitigations in test/e2e/specs/07-performance/PERFORMANCE.md @@ -38,10 +48,6 @@ Lunr is a JS search library using an inverted index. Client-side search for stat Right now we're using string literals to define HTML email templates for site mails. We should use Nunjucks with the rule-checking for valid CSS in HTML emails like we have in the corporate email footer repo. -## @TODO: Provide button to turn off animation in Hero - -"Scaling/zooming animations are problematic for accessibility, as they are a common trigger for certain types of migraine. If you need to include such animations on your website, you should provide a control to allow users to turn off animations, preferably site-wide. Also, consider making use of the prefers-reduced-motion media feature — use it to write a media query that will turn off animations if the user has reduced animation specified in their system preferences. " - ## Color vars brand primary: #001733 @@ -101,12 +107,35 @@ cat.structure: Rules related to the document's overall structure, like the prope cat.tables: Rules for data tables, including headers and associations. cat.text-alternatives: Rules for ensuring that text alternatives are provided for non-text content, such as images. +## Privacy policy errors + +fatal: /home/kevin/Repos/Webstack: '/home/kevin/Repos/Webstack' is outside repository at '/home/kevin/Repos/Webstack Builders/Corporate Website/astro.webstackbuilders.com' + +[privacy-policy-version] Could not get privacy policy version from git: Command failed: git log -1 --format=%cd --date=format:%Y-%m-%d -- /home/kevin/Repos/Webstack Builders/Corporate Website/astro.webstackbuilders.com/src/pages/privacy/index.astro + +fatal: /home/kevin/Repos/Webstack: '/home/kevin/Repos/Webstack' is outside repository at '/home/kevin/Repos/Webstack Builders/Corporate Website/astro.webstackbuilders.com' + +⚠️ Privacy policy version fallback applied: 2025-12-08 + ## @TODO: Add Check HTML Links to test workflow npm i -D check-html-links npx check-html-links _site `https://github.com/modernweb-dev/rocket/tree/main/packages/check-html-links` +22:38:43 [ERROR] [astro-link-validator] ❌ Found 2 broken links: +\n📄 index.html: + 🔗 /services/web-development + File not found: services/web-development + Text: "Improve Your Product" + 🔗 /services/consulting + File not found: services/consulting + Text: "Start from scratch" + +## Tags + +Can we add a markdown page to explain what each tag is, and use those pages instead of src/content/_tagList.ts to define the tags available? + ## Social Media Preview Cards Looking at the social-card endpoint implementation, it's designed to work with third-party screenshot services, not the social networks themselves. diff --git a/astro.config.ts b/astro.config.ts index d9f06e412..2ef4bc6fd 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -1,5 +1,6 @@ import AstroPWA from '@vite-pwa/astro' import icon from 'astro-icon' +import linkValidator from 'astro-link-validator' import lit from '@semantic-ui/astro-lit' import mdx from '@astrojs/mdx' import sentry from '@sentry/astro' @@ -8,6 +9,7 @@ import tailwindcss from '@tailwindcss/vite' import vercelStatic from '@astrojs/vercel' import vtbot from 'astro-vtbot' import { defineConfig } from 'astro/config' +import type { AstroUserConfig } from 'astro' import { fileURLToPath } from 'node:url' import type { PluginOption } from 'vite' /** @@ -22,6 +24,7 @@ import { environmentalVariablesConfig, getSentryAuthToken, getSiteUrl, + isGitHub, isUnitTest, isVercel, markdownConfig, @@ -70,6 +73,13 @@ const standardIntegrations = [ exclude: ['downloads', 'social-shares', '/articles/demo', 'testing'], }), }), + /** Validate links in built site output on astro:build:done integration hook */ + linkValidator({ + checkExternal: isGitHub(), + failOnBrokenLinks: false, + verbose: true, + }), + /** Integration to write pages.json after build so E2E tests can discover what pages exist */ pagesJsonWriter(), /** Debugging tools for Astro View Transition API */ vtbot(), @@ -81,6 +91,7 @@ export default defineConfig({ enabled: false, }, env: environmentalVariablesConfig, + markdown: markdownConfig as AstroUserConfig['markdown'], /** * Astro sets substantial Vite config internally in the framework. When you use Vitest * in an Astro project, you use Astro's getViteConfig helper to get the resolved internal diff --git a/package-lock.json b/package-lock.json index cc5e34f12..5ad124911 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,6 +35,7 @@ "@webcomponents/template-shadowroot": "^0.2.1", "astro": "5.16.4", "astro-icon": "^1.1.5", + "astro-link-validator": "github:rodgtr1/astro-link-validator", "astro-vtbot": "^2.1.9", "cross-env": "^10.1.0", "dotenv": "17.2.3", @@ -58,7 +59,6 @@ "remark-breaks": "^4.0.0", "remark-emoji": "^5.0.2", "remark-linkify-regex": "^1.2.1", - "remark-toc": "^9.0.0", "resend": "^6.5.2", "schema-dts": "^1.1.5", "sharp": "^0.34.5", @@ -7688,26 +7688,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@testing-library/preact/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@testing-library/preact/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/@testing-library/user-event": { "version": "14.6.1", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", @@ -8079,12 +8059,6 @@ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "license": "MIT" }, - "node_modules/@types/ungap__structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/ungap__structured-clone/-/ungap__structured-clone-1.2.0.tgz", - "integrity": "sha512-ZoaihZNLeZSxESbk9PUAPZOlSpcKx81I1+4emtULDVmBLkYutTcMlCj2K9VNlf9EWODxdO6gkAqEaLorXwZQVA==", - "license": "MIT" - }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -9799,6 +9773,18 @@ "@iconify/utils": "^2.1.30" } }, + "node_modules/astro-link-validator": { + "version": "1.0.0", + "resolved": "git+ssh://git@github.com/rodgtr1/astro-link-validator.git#025a27b2582234084b3676bc725418b5fc429dcd", + "license": "MIT", + "dependencies": { + "cheerio": "^1.0.0-rc.12", + "picocolors": "^1.0.0" + }, + "peerDependencies": { + "astro": "^4.0.0 || ^5.0.0" + } + }, "node_modules/astro-vtbot": { "version": "2.1.9", "resolved": "https://registry.npmjs.org/astro-vtbot/-/astro-vtbot-2.1.9.tgz", @@ -10857,24 +10843,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/cliui/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/cliui/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, "node_modules/cliui/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -10962,6 +10930,24 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -12777,26 +12763,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/eslint/node_modules/eslint-visitor-keys": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", @@ -16671,25 +16637,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-toc": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-toc/-/mdast-util-toc-7.1.0.tgz", - "integrity": "sha512-2TVKotOQzqdY7THOdn2gGzS9d1Sdd66bvxUyw3aNpWfcPXCLYSJCCgfPy30sEtuzkDraJgqF35dzgmz6xlvH/w==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/ungap__structured-clone": "^1.0.0", - "@ungap/structured-clone": "^1.0.0", - "github-slugger": "^2.0.0", - "mdast-util-to-string": "^4.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/mdn-data": { "version": "2.12.2", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", @@ -19642,20 +19589,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/remark-toc": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/remark-toc/-/remark-toc-9.0.0.tgz", - "integrity": "sha512-KJ9txbo33GjDAV1baHFze7ij4G8c7SGYoY8Kzsm2gzFpbhL/bSoVpMMzGa3vrNDSWASNd/3ppAqL7cP2zD6JIA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-toc": "^7.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -20575,26 +20508,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/smob": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz", @@ -24275,24 +24188,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/wrap-ansi-cjs/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", diff --git a/package.json b/package.json index 410a57c18..e724f173e 100644 --- a/package.json +++ b/package.json @@ -35,8 +35,8 @@ "format:code": "FORCE_COLOR=1 npx prettier --write \"@types/**/*.{js,ts}\" \"src/**/*.{js,ts,tsx,astro}\" --plugin=prettier-plugin-astro", "format:json": "FORCE_COLOR=1 npx prettier --write '**/*.json' --cache --ignore-path .gitignore", "format:style": "FORCE_COLOR=1 npx stylelint --fix \"src/**/*.{css,astro}\"", - "lint": "npm run lint:base && npm run lint:actions && npm run check", - "lint:base": "npm run lint:json && npm run lint:style && npm run lint:tsc:check && npm run lint:code", + "lint": "npm run lint:base && npm run check", + "lint:base": "npm run lint:json && npm run lint:style && npm run lint:tsc:check && npm run lint:code && npm run lint:actions", "lint:code": "npx eslint \"@types/**/*.{js,ts}\" \"src/**/*.{js,ts,tsx,astro}\" \"test/**/*.{js,ts,tsx,astro}\"", "lint:tsc:check": "tsc --noEmit -p tsconfig.json --pretty false", "lint:json": "FORCE_COLOR=1 npx prettier --write '**/*.json' --cache --ignore-path .gitignore", @@ -89,6 +89,7 @@ "@webcomponents/template-shadowroot": "^0.2.1", "astro": "5.16.4", "astro-icon": "^1.1.5", + "astro-link-validator": "github:rodgtr1/astro-link-validator", "astro-vtbot": "^2.1.9", "cross-env": "^10.1.0", "dotenv": "17.2.3", @@ -112,7 +113,6 @@ "remark-breaks": "^4.0.0", "remark-emoji": "^5.0.2", "remark-linkify-regex": "^1.2.1", - "remark-toc": "^9.0.0", "resend": "^6.5.2", "schema-dts": "^1.1.5", "sharp": "^0.34.5", diff --git a/src/assets/images/avatars/kevin-brown.jpg b/src/assets/images/avatars/kevin-brown.jpg deleted file mode 100644 index b05169c43..000000000 Binary files a/src/assets/images/avatars/kevin-brown.jpg and /dev/null differ diff --git a/src/assets/images/avatars/test-red-dot.png b/src/assets/images/avatars/test-red-dot.png deleted file mode 100644 index dbe2432f2..000000000 Binary files a/src/assets/images/avatars/test-red-dot.png and /dev/null differ diff --git a/src/assets/images/downloads/api-tool-consolidation.png b/src/assets/images/downloads/api-tool-consolidation.png new file mode 100644 index 000000000..f23367b21 Binary files /dev/null and b/src/assets/images/downloads/api-tool-consolidation.png differ diff --git a/src/assets/images/downloads/identity-security-dummies.png b/src/assets/images/downloads/identity-security-dummies.png new file mode 100644 index 000000000..f23367b21 Binary files /dev/null and b/src/assets/images/downloads/identity-security-dummies.png differ diff --git a/src/assets/images/downloads/lakehouse-analytics-guide.png b/src/assets/images/downloads/lakehouse-analytics-guide.png new file mode 100644 index 000000000..f23367b21 Binary files /dev/null and b/src/assets/images/downloads/lakehouse-analytics-guide.png differ diff --git a/src/assets/images/downloads/observability-benefits.png b/src/assets/images/downloads/observability-benefits.png new file mode 100644 index 000000000..f23367b21 Binary files /dev/null and b/src/assets/images/downloads/observability-benefits.png differ diff --git a/src/assets/images/downloads/ransomware-recovery-kit.png b/src/assets/images/downloads/ransomware-recovery-kit.png new file mode 100644 index 000000000..f23367b21 Binary files /dev/null and b/src/assets/images/downloads/ransomware-recovery-kit.png differ diff --git a/src/components/Avatar/README.md b/src/components/Avatar/README.md index 4c014627a..7ed480e3b 100644 --- a/src/components/Avatar/README.md +++ b/src/components/Avatar/README.md @@ -2,16 +2,6 @@ A reusable Avatar component for displaying user/author avatar images with automatic fallback to initials. -## Features - -- ✅ Displays avatar images from the avatars directory -- ✅ Automatic name normalization (e.g., "Chris Southam" → "chris-southam") -- ✅ Automatic fallback to initial placeholder if image doesn't exist -- ✅ Renders nothing if name is not provided -- ✅ Singleton pattern for efficient image loading -- ✅ Type-safe with TypeScript -- ✅ Lazy loading by default - ## Usage ```astro @@ -32,13 +22,6 @@ import Avatar from '@components/Avatar/index.astro' /> ``` -## Props - -| Prop | Type | Required | Description | -|---------|----------|----------|------------------------------------------------------------------| -| `name` | `string` | Yes | The person's name (automatically normalized for filename lookup) | -| `class` | `string` | No | Additional CSS classes to apply to the container | - ## Name Normalization The component automatically normalizes names to match avatar filenames: @@ -54,43 +37,9 @@ The normalization process: 3. Replaces spaces with hyphens 4. Removes non-alphanumeric characters (except hyphens) -## File Structure - -```text -src/components/Avatar/ -├── index.astro # Main Avatar component -├── avatars.ts # Avatar Manager singleton for loading images -└── README.md # This file -``` - ## Adding New Avatars 1. Add image files to `src/assets/images/avatars/` 2. Supported formats: `.webp`, `.jpg`, `.png` 3. Naming convention: Use kebab-case (e.g., `kevin-brown.webp`) 4. The Avatar Manager will automatically detect and load them - -## Styling - -The component renders with the following structure: - -```html -