Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: CI/CD Pipeline

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: "pages"
cancel-in-progress: false

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "24"

- name: Install dependencies
run: npm ci

- name: Check code formatting
run: npm run format:check

- name: Run ESLint
run: npm run lint

- name: Run Tests
run: npm test -- --coverage

- name: Upload Test Coverage Artifact
uses: actions/upload-artifact@v4
with:
name: jest-coverage-report
path: coverage/

build:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
needs: test
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Pages
uses: actions/configure-pages@v4

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: "./src"

deploy:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
29 changes: 0 additions & 29 deletions .github/workflows/ci.yml

This file was deleted.

7 changes: 7 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules/
coverage/
dist/
build/
*.min.js
*.min.css
package-lock.json
8 changes: 8 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"semi": true,
"singleQuote": false,
"tabWidth": 4,
"trailingComma": "none",
"printWidth": 100,
"arrowParens": "always"
}
56 changes: 28 additions & 28 deletions docs/project-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,60 +12,60 @@ This is a **static portfolio website** built with vanilla HTML, CSS, and JavaScr

This module manages all text updates, language detection, and metadata application without external libraries.

* **Logic:** Language priority is checked in this order: **Local Storage (key: 'language') → Browser Language → Fallback to English**.
* **Data Structure:** Translations are stored in flat JSON files within the `/locales/` directory (e.g., `en.json`, `role`).
* **HTML Attributes:** Elements are updated using custom `data-translate` attributes:
* `data-translate="key"`: Updates element's **textContent**.
* `data-translate-alt="key"`: Updates the **alt** attribute (essential for images and accessibility).
* `data-translate-html="key"`: Updates **innerHTML** (supported, currently unused).
* **SEO/Metadata:** The system dynamically updates the page `<title>`, `<meta name="description">`, and Open Graph (`og:title`, `og:description`) tags on language change.
* **Performance:** Translations are loaded **asynchronously** (`async/await`) using the native `fetch` API.
- **Logic:** Language priority is checked in this order: **Local Storage (key: 'language') → Browser Language → Fallback to English**.
- **Data Structure:** Translations are stored in flat JSON files within the `/locales/` directory (e.g., `en.json`, `role`).
- **HTML Attributes:** Elements are updated using custom `data-translate` attributes:
- `data-translate="key"`: Updates element's **textContent**.
- `data-translate-alt="key"`: Updates the **alt** attribute (essential for images and accessibility).
- `data-translate-html="key"`: Updates **innerHTML** (supported, currently unused).
- **SEO/Metadata:** The system dynamically updates the page `<title>`, `<meta name="description">`, and Open Graph (`og:title`, `og:description`) tags on language change.
- **Performance:** Translations are loaded **asynchronously** (`async/await`) using the native `fetch` API.

### Custom Dropdown (`js/custom-select.js`)

This file implements the styled dropdown menu for language selection.

* **UI Implementation:** Uses vanilla JavaScript to handle dropdown toggle and visual state management (`.open`, `.selected` classes).
* **Integration:** It integrates with the translation system by calling **`setLanguage(lang)`** from `translations.js` upon selection.
* **Language Names:** Display names (e.g., 'English', 'Español') are hardcoded in the `languageNames` object within this file.
- **UI Implementation:** Uses vanilla JavaScript to handle dropdown toggle and visual state management (`.open`, `.selected` classes).
- **Integration:** It integrates with the translation system by calling **`setLanguage(lang)`** from `translations.js` upon selection.
- **Language Names:** Display names (e.g., 'English', 'Español') are hardcoded in the `languageNames` object within this file.

### CSS Architecture (`css/style.css`)

The CSS follows a well-organized structure with clear separation of concerns.

* **Color Scheme:** Uses **CSS Variables** defined in `:root` (e.g., `--clr-navy`, `--clr-linkedin`) for easy theme consistency.
* **Icon Styling Pattern:** Icons (SVGs) use CSS filters for color manipulation across different states:
* White icon on dark background: `filter: brightness(0) invert(1)`
* Dark icon on light background: `filter: brightness(0) invert(0)`
* **Visual Pattern:** Elements like the language selector implement a "Glassmorphism" effect using `rgba()` combined with `backdrop-filter: blur(10px)`.
* **Responsiveness:** Mobile-first approach using Bootstrap utility classes and media queries for specific adjustments below 992px and 576px.
- **Color Scheme:** Uses **CSS Variables** defined in `:root` (e.g., `--clr-navy`, `--clr-linkedin`) for easy theme consistency.
- **Icon Styling Pattern:** Icons (SVGs) use CSS filters for color manipulation across different states:
- White icon on dark background: `filter: brightness(0) invert(1)`
- Dark icon on light background: `filter: brightness(0) invert(0)`
- **Visual Pattern:** Elements like the language selector implement a "Glassmorphism" effect using `rgba()` combined with `backdrop-filter: blur(10px)`.
- **Responsiveness:** Mobile-first approach using Bootstrap utility classes and media queries for specific adjustments below 992px and 576px.

---

## Conventions & Development Workflow

### Development Setup

| Task | Detail |
| :--- | :--- |
| **Cross-Platform** | Developed and maintained across **Linux, Windows, and macOS**. |
| **Dependencies** | Requires **Node.js/NPM** to run development tooling (Jest). The core site is dependency-free. |
| **Local Testing** | The project is static: open `index.html` in the browser or use a simple HTTP server. |
| **Deployment** | Git push to the main branch auto-deploys via GitHub Pages. |
| Task | Detail |
| :------------------ | :------------------------------------------------------------------------------------------------------- |
| **Cross-Platform** | Developed and maintained across **Linux, Windows, and macOS**. |
| **Dependencies** | Requires **Node.js/NPM** to run development tooling (Jest). The core site is dependency-free. |
| **Local Testing** | The project is static: open `index.html` in the browser or use a simple HTTP server. |
| **Deployment** | Git push to the main branch auto-deploys via GitHub Pages. |
| **Version Control** | **`package-lock.json`** must be committed to Git to ensure dependency stability across all environments. |

### Testing

* **Unit Tests:** JavaScript logic (`translations.js`, `custom-select.js`) is validated by `*.test.js` files.
* **Execution:** Tests must be run using **Jest** via the NPM script: `npm test`.
- **Unit Tests:** JavaScript logic (`translations.js`, `custom-select.js`) is validated by `*.test.js` files.
- **Execution:** Tests must be run using **Jest** via the NPM script: `npm test`.

### Adding New Content

* **New Translations:**
- **New Translations:**
1. Add the new key and value to **ALL** locale files (`en.json`, `es.json`, `fr.json`, `pt.json`).
2. Apply the corresponding `data-translate="keyName"` attribute to the target HTML element.
* **New Languages:**
- **New Languages:**
1. Create a new locale file in `/locales/{lang-code}.json` with **all** existing translation keys.
2. Add the language code to the `supportedLangs` array in `js/translations.js`.
3. Add the language name to the `languageNames` object in `js/custom-select.js`.
4. Add the option element to the HTML selector in `index.html`.
4. Add the option element to the HTML selector in `index.html`.
83 changes: 83 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import js from "@eslint/js";
import globals from "globals";
import json from "@eslint/json";
import prettier from "eslint-config-prettier";
import prettierPlugin from "eslint-plugin-prettier";

export default [
{
ignores: ["coverage/**", "node_modules/**"]
},
{
files: ["**/*.{js,mjs,cjs}"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: globals.browser
},
rules: {
...js.configs.recommended.rules,
"no-unused-vars": "warn",
"no-console": "off"
}
},
{
files: ["jest.config.js"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "commonjs",
globals: globals.node
},
rules: {
...js.configs.recommended.rules
}
},
{
files: ["**/*.test.js"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: {
...globals.browser,
...globals.jest,
...globals.node,
setLanguage: "readonly",
getCurrentLanguage: "readonly"
}
},
rules: {
...js.configs.recommended.rules
}
},
{
files: ["src/js/*.js"],
ignores: ["src/js/*.test.js"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: {
...globals.browser,
setLanguage: "readonly",
getCurrentLanguage: "readonly"
}
},
rules: {
...js.configs.recommended.rules
}
},
{
files: ["**/*.json"],
language: "json/json",
...json.configs.recommended
},
prettier,
{
files: ["**/*.{js,mjs,cjs}"],
plugins: {
prettier: prettierPlugin
},
rules: {
"prettier/prettier": "warn"
}
}
];
13 changes: 5 additions & 8 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
module.exports = {
testEnvironment: 'jsdom',
testMatch: ['**/*.test.js'],
collectCoverageFrom: [
'src/js/**/*.js',
'!src/js/**/*.test.js'
],
coverageDirectory: 'coverage',
verbose: true
testEnvironment: "jsdom",
testMatch: ["**/*.test.js"],
collectCoverageFrom: ["src/js/**/*.js", "!src/js/**/*.test.js"],
coverageDirectory: "coverage",
verbose: true
};
Loading