Skip to content

Commit 085f71f

Browse files
committed
feat: [#100] about page integration
1 parent 762f5aa commit 085f71f

File tree

12 files changed

+214
-255
lines changed

12 files changed

+214
-255
lines changed

src/components/pixel-grid/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ export function createPixelGrid(config: GRID_CONFIG, alignment: 'left' | 'right'
4141
function createCanvasElement(): HTMLCanvasElement {
4242
const canvas = document.createElement('canvas');
4343
canvas.className = 'pixel-grid';
44-
canvas.style.width = '100%';
45-
canvas.style.height = '100%';
4644
canvas.style.display = 'block';
4745
return canvas;
4846
}

src/content/about/index.ts

Lines changed: 51 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,53 @@
1-
import {AboutContentStructure} from "../../views";
1+
import { AboutPageContentStructure } from '../../views/about/';
22

3-
export const aboutContent: AboutContentStructure = {
4-
title: "About",
5-
subtitle: "Me, myself and I",
6-
introTagline: "Charles Doucet (cd)",
7-
introText: "I’m a Montreal-based designer with a passion for technology and interactive media, driven by my curiosity and thirst for knowledge.",
8-
gridCategory: [
9-
{
10-
title: "Education",
11-
items: [
12-
{
13-
title: "Computation Arts (BAC)",
14-
subtitle: "Concordia University",
15-
text: "2016 - 2019 / Graduated with Distinction"
16-
},
17-
{
18-
title: "Graphic Design (TEC)",
19-
subtitle: "Cégep du Vieux Montréal",
20-
text: "2011 - 2014"
21-
}
22-
]
23-
},
24-
{
25-
title: "Professional Experience",
26-
items: [
27-
{
28-
title: "Lead Designer",
29-
subtitle: "Behaviour Interactive",
30-
text: "October 2021 - Present"
31-
},
32-
{
33-
title: "UX / UI Designer",
34-
subtitle: "Voxco Software",
35-
text: "April 2015 - October 2021"
36-
},
37-
{
38-
title: "Game Developer",
39-
subtitle: "Independent",
40-
text: "April 2018 - Present"
41-
}
42-
]
43-
}
44-
],
45-
achievements: [
46-
{
47-
title: "Undergraduate Fellow Award",
48-
description: "Given by Milieux at Concordia. Became a member of TAG (Technoculture, Art and Games) research centre."
49-
},
50-
{
51-
title: "Platinum every Soulsborne games",
52-
description: "Yep, you read that right. I have earned every trophy from Demon Souls to Elden Ring (even Dark Souls 2)."
53-
}
54-
],
55-
profileLinks: [
56-
{
57-
text: "LinkedIn",
58-
link: "https://www.linkedin.com/in/charlesdouc/"
59-
},
60-
{
61-
text: "GitHub",
62-
link: "https://github.com/cdoucet/",
63-
}
64-
]
3+
export const aboutContent: AboutPageContentStructure = {
4+
manifesto: {
5+
introTitle: "About cd-labs",
6+
header: "The manifesto for <span class='highlight'>shorter games</span>",
7+
paragraphs: [
8+
"Look at the achievements for any major blockbuster game. The trophy for completing the final chapter is often one of the rarest. The data tells a clear story: a huge percentage of players never see the credits roll.",
9+
"It isn't a lack of passion; it's a lack of time. As our lives get busier, 100-hour epics filled with repetitive tasks begin to feel like a second job. The industry is selling promises of vast worlds that, for most people, will remain largely unexplored.",
10+
"This pursuit of scale also leads to bloated budgets, immense pressure on development teams, and a reliance on safe, overused formulas to justify the investment. Creativity is often the first casualty of this cycle.",
11+
"Shorter, focused games are the elegant solution. They respect the player's time and intelligence. They allow for healthier, more creative development. They deliver a complete, handcrafted story with a satisfying conclusion. This isn't about making 'less' of a game; it's about making 'more' of the parts that truly matter.",
12+
],
13+
alignment: 'right',
14+
},
15+
16+
ctaBannerA: {
17+
header: "Save your progress",
18+
body: "Don't lose your place in our story. Follow along for development checkpoints, sneak peeks, and insights into our design process.",
19+
buttons: [
20+
{ text: "Follow on Instagram", path: "#", styleType: "primary", contrastMode: 'light' },
21+
{ text: "See logs", path: "#", styleType: "secondary", contrastMode: 'light' }
22+
],
23+
alignment: 'right'
24+
},
25+
26+
path: {
27+
header: "Why I chose a <span class='highlight'>different path</span>.",
28+
paragraphs: [
29+
"With a decade of experience, I've cultivated a diverse professional background as a marketing designer, UX/UI designer for web applications, and game developer. My core passion lies in games and interactive media—a burgeoning field that's reshaping the world and fostering innovation.",
30+
"Initially, I thought my path was in AAA studios, but I soon realized that design often takes a back seat there. Large productions typically stick to proven formulas to reduce risk. This makes sense, given the millions invested in game development and the critical need for success. However, many projects ultimately fail, games are canceled, teams are laid off, and the cycle continues. That is why, after four years of valuable experience, I decided to leave the AAA world and commit to a different path.",
31+
],
32+
alignment: 'left',
33+
},
34+
35+
initiative: {
36+
header: "The <span class='highlight'>cd-labs</span> initiative",
37+
paragraphs: [
38+
"My commitment is to a different model for game development—one that’s faster, more creative, and built on a foundation of unique ideas instead of market speculation. The goal is to deliver smaller, yet complete, gaming experiences centered around innovative and engaging mechanics.",
39+
"This approach also aims to bring players closer to the development world. By working transparently and involving the community in the process, I can deliver regular, polished, and affordable games that truly respect the player’s time.",
40+
],
41+
alignment: 'right',
42+
},
43+
44+
ctaBannerB: {
45+
header: "Join our journey",
46+
body: "Be part of something exciting! Follow us on social media and stay updated with our latest news and developments.",
47+
buttons: [
48+
{ text: "Follow on Twitter", path: "#", styleType: "primary", contrastMode: 'light' },
49+
{ text: "Like on Facebook", path: "#", styleType: "secondary", contrastMode: 'light' }
50+
],
51+
alignment: 'left'
52+
},
6553
}

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import {buildMainLogo, changeLogoOnScroll} from "./components/logo";
22
import { router } from "./core/router";
3-
import {homeView, buildViewBase, renderView} from "./views";
3+
import {homeView, aboutView, buildViewBase, renderView} from "./views";
44
import {buildTopNav} from "./components/top-nav";
55

66

77
const routes = [
88
{ path: '', handler: () => renderView(homeView()) },
9+
{ path: '/about', handler: () => renderView(aboutView()) },
910
];
1011

1112
routes.forEach(route => router.registerRoute(route.path, route.handler));

src/views/about.ts

Lines changed: 0 additions & 148 deletions
This file was deleted.

src/views/about/aboutSection.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { writeParagraph, writeTitle, createWrapper } from "../utils";
2+
3+
export type AboutSectionContent = {
4+
readonly introTitle?: string;
5+
readonly header: string;
6+
readonly paragraphs: string[];
7+
readonly alignment: 'left' | 'right';
8+
}
9+
10+
// --------------------------------------------------------------------------------
11+
12+
export function createAboutSection(content: AboutSectionContent) {
13+
const section = document.createElement('section');
14+
const wrapper = createWrapper();
15+
const article = document.createElement('article');
16+
17+
section.className = `about-section ${content.alignment}`;
18+
19+
if (content.introTitle) {
20+
article.appendChild(introTitle(content.introTitle));
21+
}
22+
23+
article.appendChild(writeTitle("h2", content.header));
24+
article.appendChild(sectionParagraphs(content.paragraphs));
25+
26+
wrapper.appendChild(article);
27+
section.appendChild(wrapper);
28+
29+
return section;
30+
}
31+
32+
// --------------------------------------------------------------------------------
33+
34+
function sectionParagraphs(paragraphs: string[]) {
35+
const paragraphsContainer = document.createElement('div');
36+
paragraphsContainer.className = 'paragraphs-container';
37+
38+
paragraphs.forEach(text => {
39+
const p = writeParagraph(text);
40+
paragraphsContainer.appendChild(p);
41+
});
42+
return paragraphsContainer;
43+
}
44+
45+
function introTitle(text: string) {
46+
const intro = document.createElement('h3');
47+
intro.className = 'intro-title';
48+
intro.innerHTML = text;
49+
return intro;
50+
}

src/views/about/index.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import './styles.css';
2+
import { GRID_CONFIG } from '../../components/pixel-grid';
3+
import { createPixelGridBackground } from '../utils/backgrounds-utils';
4+
import { AboutSectionContent, createAboutSection } from "./aboutSection";
5+
import { createPixelBannerCTA, CallToActionOptions } from "../common/call-to-action";
6+
import { aboutContent } from "../../content/about";
7+
8+
const pixelGridConfigs: GRID_CONFIG = {
9+
rows: 2,
10+
colors: ['#0f0f0f', '#2a2a2a', '#181818']
11+
}
12+
13+
export type AboutPageContentStructure = {
14+
readonly manifesto: AboutSectionContent;
15+
readonly ctaBannerA: CallToActionOptions;
16+
readonly path: AboutSectionContent;
17+
readonly initiative: AboutSectionContent;
18+
readonly ctaBannerB: CallToActionOptions;
19+
}
20+
21+
// --------------------------------------------------------------------------------
22+
23+
export function aboutView() {
24+
const page = document.createElement('div');
25+
page.id = 'about-page';
26+
27+
const firstSection = createAboutSection(aboutContent.manifesto);
28+
firstSection.classList.add('first-section');
29+
firstSection.appendChild(createPixelGridBackground('left', pixelGridConfigs));
30+
31+
page.appendChild(firstSection);
32+
page.appendChild(createPixelBannerCTA(aboutContent.ctaBannerA));
33+
page.appendChild(createAboutSection(aboutContent.path));
34+
page.appendChild(createAboutSection(aboutContent.initiative));
35+
page.appendChild(createPixelBannerCTA(aboutContent.ctaBannerB));
36+
37+
return page;
38+
}
39+

0 commit comments

Comments
 (0)