diff --git a/.alexrc.js b/.alexrc.js
index 8b0fbe66..71fad3d9 100644
--- a/.alexrc.js
+++ b/.alexrc.js
@@ -13,6 +13,16 @@ module.exports = {
"yank",
"boy-girl",
"aunt-uncle",
- "savage"
+ // Below were mostly known warnings
+ "savage",
+ "daughter-son",
+ "gal-guy",
+ "obvious",
+ "dad-mom",
+ "gals-man",
+ "straightforward",
+ "clearly",
+ "prince-princess",
+ "deaf-to"
]
};
\ No newline at end of file
diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml
new file mode 100644
index 00000000..3eb13143
--- /dev/null
+++ b/.github/workflows/playwright.yml
@@ -0,0 +1,27 @@
+name: Playwright Tests
+on:
+ push:
+ branches: [ main, master ]
+ pull_request:
+ branches: [ main, master ]
+jobs:
+ test:
+ timeout-minutes: 60
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: lts/*
+ - name: Install dependencies
+ run: npm ci
+ - name: Install Playwright Browsers
+ run: npx playwright install --with-deps
+ - name: Run Playwright tests
+ run: npx playwright test
+ - uses: actions/upload-artifact@v4
+ if: ${{ !cancelled() }}
+ with:
+ name: playwright-report
+ path: playwright-report/
+ retention-days: 30
diff --git a/.gitignore b/.gitignore
index 882d842c..dc57a153 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,4 +4,10 @@ npm-debug.log
.idea/
-*.idlk
\ No newline at end of file
+*.idlk
+
+# Playwright
+/test-results/
+/playwright-report/
+/blob-report/
+/playwright/.cache/
diff --git a/docs/404.html b/docs/404.html
index e81c7aab..4e06c3dd 100644
--- a/docs/404.html
+++ b/docs/404.html
@@ -6,6 +6,7 @@
+
diff --git a/docs/about/index.html b/docs/about/index.html
index 975a2c6e..184eb6d1 100644
--- a/docs/about/index.html
+++ b/docs/about/index.html
@@ -6,6 +6,7 @@
+
diff --git a/docs/blog-archive/index.html b/docs/blog-archive/index.html
index a10e44f4..b63c102f 100644
--- a/docs/blog-archive/index.html
+++ b/docs/blog-archive/index.html
@@ -6,6 +6,7 @@
+
diff --git a/docs/blog/index.html b/docs/blog/index.html
index aca4d693..29e3eff4 100644
--- a/docs/blog/index.html
+++ b/docs/blog/index.html
@@ -6,6 +6,7 @@
+
diff --git a/docs/book-design/index.html b/docs/book-design/index.html
index 2cedd73e..9c59acf6 100644
--- a/docs/book-design/index.html
+++ b/docs/book-design/index.html
@@ -6,6 +6,7 @@
+
diff --git a/docs/branding/index.html b/docs/branding/index.html
index e2d74a22..5e809820 100644
--- a/docs/branding/index.html
+++ b/docs/branding/index.html
@@ -6,6 +6,7 @@
+
diff --git a/docs/environmental/index.html b/docs/environmental/index.html
index 07a2168a..be058175 100644
--- a/docs/environmental/index.html
+++ b/docs/environmental/index.html
@@ -6,6 +6,7 @@
+
diff --git a/docs/gradshow/index.html b/docs/gradshow/index.html
index 42c48b33..37caff33 100644
--- a/docs/gradshow/index.html
+++ b/docs/gradshow/index.html
@@ -6,6 +6,7 @@
+
@@ -86,8 +87,8 @@
Elevation diagrams of digital LED signboards.
diff --git a/docs/projects/typo-mexico-city/index.html b/docs/projects/typo-mexico-city/index.html
index 9b0ed4fd..55b049f4 100644
--- a/docs/projects/typo-mexico-city/index.html
+++ b/docs/projects/typo-mexico-city/index.html
@@ -6,6 +6,7 @@
+
diff --git a/docs/research/index.html b/docs/research/index.html
index dd5957a5..99f332c9 100644
--- a/docs/research/index.html
+++ b/docs/research/index.html
@@ -6,6 +6,7 @@
+
diff --git a/docs/strategy/index.html b/docs/strategy/index.html
index 323d84b3..17354731 100644
--- a/docs/strategy/index.html
+++ b/docs/strategy/index.html
@@ -6,6 +6,7 @@
+
diff --git a/docs/type-design/index.html b/docs/type-design/index.html
index 0b011f34..07210a13 100644
--- a/docs/type-design/index.html
+++ b/docs/type-design/index.html
@@ -6,6 +6,7 @@
+
diff --git a/package-lock.json b/package-lock.json
index dcffb10a..119250b1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,8 +10,10 @@
"license": "MIT",
"devDependencies": {
"@11ty/eleventy": "^2.0.1",
+ "@playwright/test": "^1.54.2",
"@tailwindcss/forms": "^0.4.0",
"@tailwindcss/typography": "^0.5.2",
+ "@types/node": "^24.2.0",
"alex": "^11.0.0",
"autoprefixer": "^10.4.2",
"concurrently": "^7.2.2",
@@ -510,6 +512,22 @@
"node": ">=14"
}
},
+ "node_modules/@playwright/test": {
+ "version": "1.54.2",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.2.tgz",
+ "integrity": "sha512-A+znathYxPf+72riFd1r1ovOLqsIIB0jKIoPjyK2kqEIe30/6jF6BC7QNluHuwUmsD2tv1XZVugN8GqfTMOxsA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright": "1.54.2"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@pnpm/config.env-replace": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz",
@@ -754,10 +772,14 @@
}
},
"node_modules/@types/node": {
- "version": "18.16.18",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.18.tgz",
- "integrity": "sha512-/aNaQZD0+iSBAGnvvN2Cx92HqE5sZCPZtx2TsK+4nvV23fFe09jVDvpArXr2j9DnYlzuU9WuoykDDc6wqvpNcw==",
- "dev": true
+ "version": "24.2.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.0.tgz",
+ "integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.10.0"
+ }
},
"node_modules/@types/normalize-package-data": {
"version": "2.4.1",
@@ -6051,6 +6073,38 @@
"node": ">=0.10.0"
}
},
+ "node_modules/playwright": {
+ "version": "1.54.2",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.2.tgz",
+ "integrity": "sha512-Hu/BMoA1NAdRUuulyvQC0pEqZ4vQbGfn8f7wPXcnqQmM+zct9UliKxsIkLNmz/ku7LElUNqmaiv1TG/aL5ACsw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright-core": "1.54.2"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.54.2",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.2.tgz",
+ "integrity": "sha512-n5r4HFbMmWsB4twG7tJLDN9gmBUeSPcsBZiWSE4DnYz9mJMAFqr2ID7+eGC9kpEnxExJ1epttwR59LEWCk8mtA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/please-upgrade-node": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz",
@@ -7935,6 +7989,13 @@
"node": ">=0.8.0"
}
},
+ "node_modules/undici-types": {
+ "version": "7.10.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
+ "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/unherit": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/unherit/-/unherit-3.0.0.tgz",
@@ -8012,6 +8073,16 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/unified-engine/node_modules/@types/node": {
+ "version": "18.19.121",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.121.tgz",
+ "integrity": "sha512-bHOrbyztmyYIi4f1R0s17QsPs1uyyYnGcXeZoGEd227oZjry0q6XQBQxd82X1I57zEfwO8h9Xo+Kl5gX1d9MwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
"node_modules/unified-engine/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
@@ -8091,6 +8162,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/unified-engine/node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/unified-engine/node_modules/yaml": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz",
diff --git a/package.json b/package.json
index a3c8b9b0..d3b942be 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,8 @@
"dev": "concurrently -k \"npm:start\" \"npm:postcss:w\"",
"start": "npm run-script postcss && npx @11ty/eleventy --serve --incremental --input=src --output=docs",
"build": "npx @11ty/eleventy --input=src --output=docs && npm run-script postcss",
- "test": "alex --html ./docs/",
+ "posttest": "alex --html ./docs/",
+ "test": "npx playwright test",
"postcss": "postcss src/styles.css -o docs/dist/tailwind.css",
"postcss:w": "postcss -w src/styles.css -o docs/dist/tailwind.css --poll",
"clean": "rimraf docs"
@@ -24,8 +25,10 @@
"homepage": "https://github.com/shakeelmohamed/beta#readme",
"devDependencies": {
"@11ty/eleventy": "^2.0.1",
+ "@playwright/test": "^1.54.2",
"@tailwindcss/forms": "^0.4.0",
"@tailwindcss/typography": "^0.5.2",
+ "@types/node": "^24.2.0",
"alex": "^11.0.0",
"autoprefixer": "^10.4.2",
"concurrently": "^7.2.2",
diff --git a/playwright.config.js b/playwright.config.js
new file mode 100644
index 00000000..7a96fcaa
--- /dev/null
+++ b/playwright.config.js
@@ -0,0 +1,81 @@
+// @ts-check
+import { defineConfig, devices } from '@playwright/test';
+
+/**
+ * Read environment variables from file.
+ * https://github.com/motdotla/dotenv
+ */
+// import dotenv from 'dotenv';
+// import path from 'path';
+// dotenv.config({ path: path.resolve(__dirname, '.env') });
+
+/**
+ * @see https://playwright.dev/docs/test-configuration
+ */
+export default defineConfig({
+ testDir: './tests',
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: process.env.CI ? 2 : 0,
+ /* Opt out of parallel tests on CI. */
+ workers: process.env.CI ? 1 : undefined,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: 'html',
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ // baseURL: 'http://localhost:3000',
+
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: 'on-first-retry',
+ },
+
+ /* Configure projects for major browsers */
+ projects: [
+ {
+ name: 'chromium',
+ use: { ...devices['Desktop Chrome'] },
+ },
+
+ // {
+ // name: 'firefox',
+ // use: { ...devices['Desktop Firefox'] },
+ // },
+
+ // {
+ // name: 'webkit',
+ // use: { ...devices['Desktop Safari'] },
+ // },
+
+ /* Test against mobile viewports. */
+ // {
+ // name: 'Mobile Chrome',
+ // use: { ...devices['Pixel 5'] },
+ // },
+ // {
+ // name: 'Mobile Safari',
+ // use: { ...devices['iPhone 12'] },
+ // },
+
+ /* Test against branded browsers. */
+ // {
+ // name: 'Microsoft Edge',
+ // use: { ...devices['Desktop Edge'], channel: 'msedge' },
+ // },
+ // {
+ // name: 'Google Chrome',
+ // use: { ...devices['Desktop Chrome'], channel: 'chrome' },
+ // },
+ ],
+
+ /* Run your local dev server before starting the tests */
+ webServer: {
+ command: 'npm run start',
+ url: 'http://localhost:8080',
+ reuseExistingServer: !process.env.CI,
+ },
+});
+
diff --git a/src/_includes/layouts/super.pug b/src/_includes/layouts/super.pug
index bc99287d..3797a6e0 100644
--- a/src/_includes/layouts/super.pug
+++ b/src/_includes/layouts/super.pug
@@ -20,6 +20,7 @@ html(lang="en")
meta(http-equiv="X-UA-Compatible", content="chrome=1")
meta(name="viewport" content="width=device-width, initial-scale=1.0")
meta(name="description" content=description)
+ meta(name="author" content="Shakeel Mohamed")
//- opengraph
meta(property="og:url" content=metadata.baseurl + page.url)
diff --git a/src/projects/midjourney/index.pug b/src/projects/midjourney/index.pug
index bc6a3e59..fb66089e 100644
--- a/src/projects/midjourney/index.pug
+++ b/src/projects/midjourney/index.pug
@@ -76,4 +76,4 @@ block content
.grid-3
+portfolioImg("./img/MJ_egd_walls.png", "", "Elevation diagrams of sample environmental wall graphics.")
+portfolioImg("./img/MJ_digital_signage_diagram.png", "", "Elevation diagrams of digital LED signboards.")
- +portfolioImg("./img/MJ_installation_diagram.png", "", "Elevation diagrams of the interactive installation wall graphics.")
\ No newline at end of file
+ +portfolioImg("./img/MJ_installation_diagram.png", "", "Elevation diagrams of the interactive installation.")
\ No newline at end of file
diff --git a/src/projects/thesis/index.pug b/src/projects/thesis/index.pug
index cbc9779c..f09c7b14 100644
--- a/src/projects/thesis/index.pug
+++ b/src/projects/thesis/index.pug
@@ -16,7 +16,6 @@ media:
- environmental
goal: Expand the perceived value of designers.
recognition: Presented initial thesis research at Graph*c Content, a conference hosted by AIGA Los Angeles.
-thanks: Special thanks to my thesis advisor Michael Neal, my thesis pod group (Hayley (Haowen) An, Ana Gwyn Wilson, Avantika (Yajing) Wu, Jen (Geyuzhen) Zhu), Sheharazad Fleming, Cheri Gray, Petrula Vrontikis, Martin Grasser, Robbie Nock, Simon Johnston, Paul Mendoza, Rubina Chadha, Dr. Cheryl D. Miller, Greg Lindy, Milka Broukhim, Gloria Kondrup, Jose Caballer, Eilís Russell, Dr. Dori Tunstall, Erika Abrams, Ali Torbati, Ivan J. Cruz, Amanda Mei, Reuben Merringer, Carolina Uscategui, Vy Huynh, the ArtCenter Gx Grind Club, and staff members of AIGA Los Angeles who supported the 2024 Graph*c Content design conference.
position: 3
---
include ../../_includes/project_utils.pug
@@ -67,4 +66,11 @@ block content
h2 Thank you Michael and PB & Magick!
.grid-2
+portfolioImg("./img/thesis_pod.png", "", "My amazing thesis pod! Led by our advisor, professor Michael Neal. At the halfway point, after our fall semester final presentation.")
- +portfolioImg("./img/thesis_pod_final.png", "", "After our final presentation! Thank you is not enough to Michael and my incredibly supportive peers.")
\ No newline at end of file
+ +portfolioImg("./img/thesis_pod_final.png", "", "After our final presentation! Thank you is not enough to Michael and my incredibly supportive peers.")
+ .grid-2
+ div
+ p.bold Acknowledgements
+ p Special thanks to my thesis advisor Michael Neal, my thesis pod group (Hayley (Haowen) An, Ana Gwyn Wilson, Avantika (Yajing) Wu, Jen (Geyuzhen) Zhu), Sheharazad Fleming, Cheri Gray, Petrula Vrontikis, Martin Grasser, Robbie Nock, Simon Johnston, Paul Mendoza, Rubina Chadha, Dr. Cheryl D. Miller, Greg Lindy, Milka Broukhim, Gloria Kondrup, Jose Caballer, Eilís Russell, Dr. Dori Tunstall, Erika Abrams, Ali Torbati, Ivan J. Cruz, Amanda Mei, Reuben Merringer, Carolina Uscategui, Vy Huynh, the ArtCenter Gx Grind Club, and staff members of AIGA Los Angeles who supported the 2024 Graph*c Content design conference.
+ div
+ p.bold Copyright
+ p Copyright © 2025 Shakeel Mohamed all rights reserved. No part of this thesis project may be reproduced or transmitted in any form by any electronic or mechanical means, which may include photocopying, recording, or any other information storage and retrieval system, or otherwise without written permission from the publisher.
diff --git a/tests/home.spec.js b/tests/home.spec.js
new file mode 100644
index 00000000..a1509d05
--- /dev/null
+++ b/tests/home.spec.js
@@ -0,0 +1,38 @@
+// @ts-check
+import { test, expect } from '@playwright/test';
+
+// TODO: use this workflow for units
+/*
+const Eleventy = require("@11ty/eleventy");
+
+test("Build generates pages", async () => {
+ const elev = new Eleventy("src", "dist");
+ await elev.write();
+
+ const html = fs.readFileSync("dist/about/index.html", "utf8");
+ expect(html).toContain("About Us");
+});
+ */
+
+test('homepage has title', async ({ page }) => {
+ await page.goto('http://localhost:8080');
+ await expect(page).toHaveTitle("Shakeel Mohamed – Strategic Brand Designer in Los Angeles");
+});
+
+
+// test('has title', async ({ page }) => {
+// await page.goto('https://playwright.dev/');
+
+// // Expect a title "to contain" a substring.
+// await expect(page).toHaveTitle(/Playwright/);
+// });
+
+// test('get started link', async ({ page }) => {
+// await page.goto('https://playwright.dev/');
+
+// // Click the get started link.
+// await page.getByRole('link', { name: 'Get started' }).click();
+
+// // Expects page to have a heading with the name of Installation.
+// await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
+// });
diff --git a/todo.md b/todo.md
index bde36caf..6cbd0674 100644
--- a/todo.md
+++ b/todo.md
@@ -23,7 +23,6 @@ https://cardiff.marketing/pug-in-eleventy-making-it-work/
## Bio revision
- [ ] check OG images for projects, then posts, etc. Consider gif/video versions
-- [ ]
- [ ] Experience... consider Parenthetical titles or in 1 line summary at the top: Product Manager (Functionally leading roadmap without formal title)
- [ ] Future bio update: I’m interested in environmental design, creative technology, and Arabic type design.
- [ ] Can set up thesis in my bio a bit to speak more maturely as being an advocate and helping others to become advocates.