Skip to content

Commit 184ef5e

Browse files
committed
Showcase current apps on work page
1 parent 5763874 commit 184ef5e

1 file changed

Lines changed: 73 additions & 26 deletions

File tree

src/pages/work.astro

Lines changed: 73 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,45 @@ const apps = [
88
number: "01",
99
name: "QuackNotes",
1010
type: "Notes · Apple platforms",
11-
description: "Fast, file-based notes in plain text, Markdown, and code—with no accounts, proprietary format, or lock-in.",
11+
description: "A fast, file-based notes app for people who want to own their writing. Work in plain text, Markdown, and code without accounts, proprietary formats, or lock-in.",
12+
features: ["Plain-text files", "Markdown & code", "No account"],
13+
image: "/assets/apps/quacknotes.webp",
14+
imageAlt: "QuackNotes running on macOS with a note and source code open",
15+
imageWidth: 1011,
16+
imageHeight: 632,
17+
siteLabel: "quacknotes.app",
1218
url: "https://quacknotes.app",
19+
appUrl: undefined,
1320
color: "yellow",
1421
},
1522
{
1623
number: "02",
1724
name: "Clink Clink",
1825
type: "Finance · Desktop & web",
19-
description: "A private expense planner for projecting balances, testing financial scenarios, and understanding loans before life happens.",
26+
description: "A privacy-first expense planner that projects your balance before life happens. Test financial scenarios, understand loans, and keep every number on your device.",
27+
features: ["Local data", "Scenario planning", "Desktop & web"],
28+
image: "/assets/apps/clinkclink.png",
29+
imageAlt: "Clink Clink showing a financial planning dashboard",
30+
imageWidth: 1280,
31+
imageHeight: 713,
32+
siteLabel: "clinkclink.app",
2033
url: "https://clinkclink.app",
34+
appUrl: undefined,
2135
color: "blue",
2236
},
2337
{
2438
number: "03",
2539
name: "Punchboard",
2640
type: "Time & pay · Open source",
27-
description: "A local-first calendar for tracking work hours, vacation, sick days, and estimated gross and net pay.",
41+
description: "A calm, local-first calendar for tracking work hours, vacation, and sick days while estimating gross and net pay—all without sending your data to a server.",
42+
features: ["Local-first", "Open source", "Web & desktop"],
43+
image: "/assets/apps/punchboard.png",
44+
imageAlt: "Punchboard showing work hours, pay estimates, and a monthly calendar",
45+
imageWidth: 1280,
46+
imageHeight: 800,
47+
siteLabel: "quackbyte.dev/punchboard",
2848
url: "https://quackbyte.dev/punchboard/",
49+
appUrl: "https://quackbyte.dev/punchboard/app/",
2950
color: "dark",
3051
},
3152
];
@@ -46,15 +67,25 @@ const apps = [
4667
</div>
4768
<div class="app-list">
4869
{apps.map((app) => (
49-
<a class:list={["app-row", app.color]} href={app.url} target="_blank" rel="noreferrer">
50-
<span class="eyebrow">{app.number}</span>
51-
<div class="app-copy">
52-
<strong>{app.name}</strong>
53-
<span class="app-type">{app.type}</span>
54-
<p>{app.description}</p>
70+
<article class:list={["app-card", app.color]}>
71+
<div class="app-content">
72+
<p class="eyebrow">{app.number} · Current product</p>
73+
<h3>{app.name}</h3>
74+
<p class="app-type">{app.type}</p>
75+
<p class="app-description">{app.description}</p>
76+
<ul class="app-features" aria-label={`${app.name} highlights`}>
77+
{app.features.map((feature) => <li>{feature}</li>)}
78+
</ul>
79+
<div class="app-links">
80+
<a href={app.url} target="_blank" rel="noreferrer">Visit product <span aria-hidden="true">↗</span></a>
81+
{app.appUrl && <a href={app.appUrl} target="_blank" rel="noreferrer">Open the app <span aria-hidden="true">↗</span></a>}
82+
</div>
5583
</div>
56-
<span class="app-action">Visit app <b aria-hidden="true">↗</b></span>
57-
</a>
84+
<a class="app-media" href={app.url} target="_blank" rel="noreferrer" aria-label={`Visit ${app.name}`}>
85+
<div class:list={["window-bar", { dark: app.color === "dark" }]} aria-hidden="true"><span></span><span></span><span></span><b>{app.siteLabel}</b></div>
86+
<img src={app.image} alt={app.imageAlt} width={app.imageWidth} height={app.imageHeight} loading="lazy" />
87+
</a>
88+
</article>
5889
))}
5990
</div>
6091
</section>
@@ -85,26 +116,42 @@ const apps = [
85116
section + section { margin-top: 10rem; }
86117
.section-heading { display: grid; gap: 1rem; margin-bottom: 3rem; }
87118
.section-heading h2 { font-size: var(--text-4xl); }
88-
.app-list { border-top: 1px solid var(--ink); }
89-
.app-row { display: grid; grid-template-columns: 2.5rem minmax(0, 1fr); align-items: start; gap: 1rem; padding: 1.8rem 1rem; border-bottom: 1px solid var(--ink); text-decoration: none; transition: padding var(--theme-transition), background var(--theme-transition); }
90-
.app-copy { min-width: 0; }
91-
.app-copy strong { display: block; font-family: var(--font-brand); font-size: clamp(1.7rem, 4vw, 3.6rem); letter-spacing: -0.04em; line-height: 1; white-space: nowrap; }
92-
.app-type { display: block; margin-top: 0.65rem; color: var(--muted); font-family: var(--font-mono); font-size: 0.68rem; text-transform: uppercase; }
93-
.app-copy p { max-width: 46rem; margin-top: 1rem; color: var(--muted); font-size: clamp(0.95rem, 1.4vw, 1.08rem); line-height: 1.55; }
94-
.app-action { grid-column: 2; justify-self: start; padding-bottom: 0.2rem; border-bottom: 1px solid currentColor; font-family: var(--font-mono); font-size: 0.72rem; white-space: nowrap; }
95-
.app-action b { font-size: 1rem; }
96-
.app-row:hover { padding-inline: 1.5rem; }
97-
.app-row.yellow:hover { background: #ffcf3f; }
98-
.app-row.blue:hover { background: #bfe5ff; }
99-
.app-row.dark:hover { background: #101827; color: #e7edf5; }
100-
.app-row.dark:hover .app-type, .app-row.dark:hover .app-copy p { color: #93a3ba; }
119+
.app-list { display: grid; gap: clamp(3rem, 7vw, 6rem); }
120+
.app-card { display: grid; gap: 2.5rem; overflow: hidden; padding: clamp(1.5rem, 4vw, 3rem); border: 1px solid var(--ink); border-radius: 1.5rem; }
121+
.app-card.yellow { background: #ffcf3f; }
122+
.app-card.blue { background: #bfe5ff; }
123+
.app-card.dark { border-color: #26344b; background: #101827; color: #e7edf5; }
124+
.app-content { min-width: 0; align-self: center; }
125+
.app-content h3 { margin-top: 1.2rem; font-size: clamp(2.6rem, 6vw, 5rem); letter-spacing: -0.055em; line-height: 0.95; white-space: nowrap; }
126+
.app-type { margin-top: 1rem; color: #4b473d; font-family: var(--font-mono); font-size: 0.7rem; text-transform: uppercase; }
127+
.app-description { max-width: 37rem; margin-top: 1.4rem; color: #3e3b32; font-size: clamp(1rem, 1.5vw, 1.15rem); line-height: 1.55; }
128+
.dark .app-type, .dark .app-description { color: #a6b3c7; }
129+
.app-features { display: flex; flex-wrap: wrap; gap: 0.55rem; margin-top: 1.4rem; padding: 0; list-style: none; }
130+
.app-features li { padding: 0.45rem 0.65rem; border: 1px solid currentColor; border-radius: 999px; font-family: var(--font-mono); font-size: 0.62rem; }
131+
.app-links { display: flex; flex-wrap: wrap; gap: 1.5rem; margin-top: 1.8rem; }
132+
.app-links a { padding-bottom: 0.25rem; border-bottom: 1px solid currentColor; font-family: var(--font-mono); font-size: 0.74rem; text-decoration: none; }
133+
.app-media { min-width: 0; align-self: center; overflow: hidden; border: 1px solid var(--ink); border-radius: 0.85rem; background: #222; box-shadow: 12px 14px 0 rgba(21, 23, 20, 0.18); text-decoration: none; transform: rotate(1.2deg); }
134+
.app-card:nth-child(even) .app-media { transform: rotate(-1.2deg); }
135+
.dark .app-media { border-color: #31405a; box-shadow: 12px 14px 0 rgba(0, 0, 0, 0.24); }
136+
.window-bar { display: flex; align-items: center; gap: 0.35rem; height: 2rem; padding-inline: 0.65rem; background: var(--white); color: var(--ink); font-family: var(--font-mono); font-size: 0.62rem; }
137+
.window-bar.dark { border-bottom: 1px solid #28364d; background: #0d1420; color: #93a3ba; }
138+
.window-bar span { width: 0.55rem; aspect-ratio: 1; border: 1px solid currentColor; border-radius: 50%; }
139+
.window-bar b { margin-left: auto; overflow: hidden; font-weight: 400; text-overflow: ellipsis; white-space: nowrap; }
140+
.app-media img { display: block; width: 100%; height: auto; }
101141
.archive-grid { display: grid; gap: 1.5rem; }
102142
.archive-card { display: grid; grid-template-rows: 15rem auto; overflow: hidden; border: 1px solid var(--ink); border-radius: 1rem; background: var(--white); text-decoration: none; transition: transform var(--theme-transition), box-shadow var(--theme-transition); }
103143
.archive-card:hover { transform: translateY(-4px); box-shadow: 7px 7px 0 var(--ink); }
104144
.archive-card img { width: 100%; height: 100%; object-fit: cover; border-bottom: 1px solid var(--ink); filter: saturate(0.75); }
105145
.archive-card div { padding: 1.3rem; }
106146
.archive-card h3 { margin-top: 1rem; font-size: var(--text-xl); letter-spacing: -0.03em; }
107147
.archive-card p { margin-top: 0.7rem; color: var(--muted); }
108-
@media (min-width: 42rem) { .app-row { grid-template-columns: 3rem minmax(0, 1fr) auto; align-items: center; padding-block: 2.2rem; } .app-action { grid-column: auto; align-self: center; } }
109-
@media (min-width: 52rem) { .section-heading { grid-template-columns: 0.55fr 1.45fr; } .archive-grid { grid-template-columns: repeat(2, 1fr); } }
148+
@media (min-width: 52rem) {
149+
.section-heading { grid-template-columns: 0.55fr 1.45fr; }
150+
.app-card { grid-template-columns: minmax(18rem, 0.78fr) minmax(0, 1.22fr); min-height: 35rem; gap: 3rem; padding-left: clamp(2.5rem, 5vw, 5rem); }
151+
.app-card .app-media { margin-right: -12%; }
152+
.app-card:nth-child(even) { grid-template-columns: minmax(0, 1.22fr) minmax(18rem, 0.78fr); padding-right: clamp(2.5rem, 5vw, 5rem); padding-left: clamp(1.5rem, 4vw, 3rem); }
153+
.app-card:nth-child(even) .app-content { order: 2; }
154+
.app-card:nth-child(even) .app-media { order: 1; margin-right: 0; margin-left: -12%; }
155+
.archive-grid { grid-template-columns: repeat(2, 1fr); }
156+
}
110157
</style>

0 commit comments

Comments
 (0)