Skip to content

Commit c8141f2

Browse files
Merge pull request #235 from CodeForPhilly/feat/slack-header-link
feat(web): add a Slack link to the header and mobile menu
2 parents 0911423 + 0e32245 commit c8141f2

5 files changed

Lines changed: 148 additions & 17 deletions

File tree

‎apps/web/src/components/AppHeader.tsx‎

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
import { Separator } from '@/components/ui/separator';
2020
import { SearchBox } from '@/components/SearchBox';
2121
import { GitHubIcon } from '@/components/icons/GitHubIcon';
22+
import { SlackIcon } from '@/components/icons/SlackIcon';
2223
import { useAuth } from '@/hooks/useAuth';
2324

2425
const GITHUB_URL = 'https://github.com/CodeForPhilly';
@@ -192,12 +193,26 @@ const navLinkClass = ({ isActive }: { isActive: boolean }) =>
192193
isActive ? 'text-primary' : 'text-muted-foreground'
193194
}`;
194195

196+
function SlackLink() {
197+
return (
198+
// A plain anchor: /chat is a server redirect the router can't resolve
199+
// (specs/screens/chat.md).
200+
<Button asChild variant="ghost" size="icon-sm">
201+
<a
202+
href="/chat"
203+
target="_blank"
204+
rel="noopener noreferrer"
205+
aria-label="Join Code for Philly on Slack (opens in new tab)"
206+
>
207+
<SlackIcon />
208+
</a>
209+
</Button>
210+
);
211+
}
212+
195213
function GitHubLink() {
196214
return (
197-
// Desktop-only: between md and lg the header has no room for it (the
198-
// utility cluster would push "Help Wanted" onto two lines); the mobile
199-
// sheet carries its own GitHub row.
200-
<Button asChild variant="ghost" size="icon-sm" className="hidden lg:inline-flex">
215+
<Button asChild variant="ghost" size="icon-sm">
201216
<a
202217
href={GITHUB_URL}
203218
target="_blank"
@@ -245,7 +260,7 @@ export function AppHeader() {
245260
flex/gap spacing moves to the <ul>; the <li>s contribute none. */}
246261
<nav
247262
aria-label="Primary navigation"
248-
className="hidden md:block ml-4 flex-1"
263+
className="hidden lg:block ml-4 flex-1"
249264
>
250265
<ul className="flex items-center gap-2">
251266
<li>
@@ -274,12 +289,13 @@ export function AppHeader() {
274289
</ul>
275290
</nav>
276291

277-
{/* Desktop utility cluster: GitHub, search, auth, then the Volunteer
292+
{/* Desktop utility cluster: Slack, GitHub, search, auth, then the Volunteer
278293
CTA pinned rightmost (specs/behaviors/app-shell.md). */}
279294
<nav
280295
aria-label="Utility"
281-
className="hidden md:flex items-center gap-2 ml-auto"
296+
className="hidden lg:flex items-center gap-2 ml-auto"
282297
>
298+
<SlackLink />
283299
<GitHubLink />
284300
<SearchBox />
285301
<AuthControls />
@@ -293,7 +309,7 @@ export function AppHeader() {
293309
</nav>
294310

295311
{/* Mobile: auth + hamburger */}
296-
<div className="flex md:hidden items-center gap-2 ml-auto">
312+
<div className="flex lg:hidden items-center gap-2 ml-auto">
297313
<AuthControls />
298314
<Sheet open={mobileOpen} onOpenChange={setMobileOpen}>
299315
<SheetTrigger asChild>
@@ -392,6 +408,18 @@ export function AppHeader() {
392408
</ul>
393409
<Separator />
394410
<ul className="flex flex-col gap-2">
411+
<li>
412+
<a
413+
href="/chat"
414+
target="_blank"
415+
rel="noopener noreferrer"
416+
className="block text-sm font-medium text-muted-foreground hover:text-primary"
417+
onClick={() => setMobileOpen(false)}
418+
>
419+
Slack
420+
<span className="sr-only"> (opens in new tab)</span>
421+
</a>
422+
</li>
395423
<li>
396424
<a
397425
href={GITHUB_URL}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/** Slack mark (monochrome), sized to sit inline with text and icon buttons. */
2+
export function SlackIcon() {
3+
return (
4+
<svg
5+
xmlns="http://www.w3.org/2000/svg"
6+
width="18"
7+
height="18"
8+
viewBox="0 0 24 24"
9+
fill="currentColor"
10+
aria-hidden="true"
11+
>
12+
<path d="M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312zM18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834zM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312zM15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313z" />
13+
</svg>
14+
);
15+
}

‎plans/chat-links-full-navigation.md‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
---
2-
status: in-progress
2+
status: done
33
depends: [chat-redirect]
44
specs:
55
- specs/screens/chat.md
66
issues: [230]
7+
pr: 233
78
---
89

910
# Plan: In-app links to /chat navigate to the server
@@ -36,10 +37,10 @@ a lint rule rejects router links whose literal target starts with `/chat`,
3637

3738
## Validation
3839

39-
- [ ] `npm run type-check && npm run lint && npm test` clean.
40-
- [ ] The rule flags the five links before the fix (checked by running
40+
- [x] `npm run type-check && npm run lint && npm test` clean.
41+
- [x] The rule flags the five links before the fix (checked by running
4142
lint against the unfixed tree).
42-
- [ ] In the built app, clicking footer "Slack" leaves the SPA for the
43+
- [x] In the built app, clicking footer "Slack" leaves the SPA for the
4344
Slack SSO start URL instead of rendering the 404 page.
4445

4546
## Risks / unknowns
@@ -49,4 +50,14 @@ a lint rule rejects router links whose literal target starts with `/chat`,
4950

5051
## Notes
5152

53+
- Five links, not the three the issue named: the project page's "Chat
54+
Channel" button and the project card's "Chat" had the same bug.
55+
- Verified on the devbox build of develop at 0911423: clicking the footer
56+
"Slack" left the SPA through Slack's SSO start to the production IdP's
57+
sign-in (Slack's IdP is codeforphilly.org) instead of the 404 page.
58+
- The Vite dev server doesn't proxy `/chat`, so this can't be checked
59+
under `npm run dev`; it needs the built app.
60+
5261
## Follow-ups
62+
63+
- **None.**

‎plans/slack-header-link.md‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
---
2+
status: done
3+
depends: [chat-links-full-navigation]
4+
specs:
5+
- specs/behaviors/app-shell.md
6+
issues: [231]
7+
pr: 235
8+
---
9+
10+
# Plan: Slack in the header
11+
12+
## Scope
13+
14+
The only site-wide way into Slack is the footer's "Slack" link, which
15+
newcomers at a hack night don't find. Put Slack in the header.
16+
17+
**In:** a Slack icon link in the desktop utility cluster, before GitHub;
18+
a "Slack" row in the mobile sheet before GitHub. The desktop header now
19+
starts at lg instead of md: at 768px the header already overflowed
20+
(content links wrapping, Volunteer cut off), and there was no room for
21+
another icon. Both are full-page navigations to `/chat`
22+
in a new tab, like the GitHub link.
23+
24+
**Out:** a text "Chat" item in the content cluster (considered; an icon
25+
next to GitHub reads as "where the community lives" without crowding the
26+
content links); changing `/chat` itself.
27+
28+
## Implements
29+
30+
- [behaviors/app-shell.md](../specs/behaviors/app-shell.md#center--right-at--lg) —
31+
utility cluster and mobile sheet.
32+
33+
## Approach
34+
35+
1. `SlackIcon` beside `GitHubIcon` (monochrome mark, `currentColor`).
36+
2. `SlackLink` in `AppHeader` mirroring `GitHubLink`; a sheet row
37+
mirroring the GitHub row.
38+
3. Header breakpoint md → lg; GitHub loses its lg-only class, since the
39+
whole desktop header is lg-only now.
40+
41+
## Validation
42+
43+
- [x] `npm run type-check && npm run lint && npm test` clean.
44+
- [x] At 1024px and 1280px the desktop header keeps every content link on
45+
one line with Slack and GitHub showing; at 768px the mobile header shows.
46+
- [x] At 390px the sheet lists Slack before GitHub.
47+
- [x] Clicking the header icon opens the Slack SSO start in a new tab.
48+
49+
## Risks / unknowns
50+
51+
- Tablets in portrait lose the inline nav for the hamburger. Accepted:
52+
the alternative was a header that runs off the screen.
53+
54+
## Notes
55+
56+
- The 768px overflow predates this change: develop already wrapped "Help
57+
Wanted" and pushed Volunteer off the edge there, most likely since
58+
Events joined the content cluster. Moving the breakpoint fixed that too.
59+
- The header icon is the same `/chat` anchor as the footer link verified
60+
in [chat-links-full-navigation](chat-links-full-navigation.md), plus
61+
`target="_blank"`; checked by markup rather than a second click-through.
62+
- Chose an icon beside GitHub over a "Chat" text item in the content
63+
cluster.
64+
65+
## Follow-ups
66+
67+
- **None.**

‎specs/behaviors/app-shell.md‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ Sticky at the top of the viewport. Background opaque, slight shadow on scroll.
3333
- Logo (`/img/logo.png`) — 32px tall, links to `/`
3434
- Site name "Code for Philly" — visible at ≥ md, hidden below to save space
3535

36-
### Center / right at ≥ md
36+
### Center / right at ≥ lg
3737

3838
Two clusters. The **content cluster** sits next to the logo; the **utility
39-
cluster** is pinned to the right edge and carries the outbound link, search,
39+
cluster** is pinned to the right edge and carries the outbound links, search,
4040
auth, and the call to action.
4141

4242
Content cluster, items in this order:
@@ -53,6 +53,7 @@ Utility cluster, items in this order (left to right):
5353

5454
| Item | Target | Style |
5555
| ---- | ------ | ----- |
56+
| Slack | `/chat` | icon-only link, accessible name "Join Code for Philly on Slack", opens in a new tab; a full-page navigation to the server redirect ([chat.md](../screens/chat.md#linking-to-it-from-the-app)) |
5657
| GitHub | `https://github.com/CodeForPhilly` | icon-only external link, accessible name "Code for Philly on GitHub", opens in a new tab |
5758
| Search 🔍 | inline expand | icon button |
5859
| Sign in / account menu | see [Auth controls](#auth-controls) | button / avatar dropdown |
@@ -62,6 +63,15 @@ Utility cluster, items in this order (left to right):
6263
control rather than among the content links so the call to action reads as the
6364
header's terminal step, not as one more section.
6465

66+
**Slack is always one step from the header.** It is where the community
67+
actually works, and a newcomer looking for it should not have to scroll to
68+
the footer. The footer's Connect column keeps its Slack link too.
69+
70+
**The desktop header starts at lg (1024px).** Below that the content
71+
cluster and the utility cluster do not fit on one line (the content links
72+
wrap and Volunteer runs off the edge), so tablets in portrait get the
73+
mobile header and its sheet.
74+
6575
### About dropdown
6676

6777
- Mission → `/pages/mission`
@@ -93,9 +103,9 @@ sheet.
93103
- **Administrator:** Staff items plus "Admin" section:
94104
- "Manage members" → `/members?staff=true&accountLevel=all`
95105

96-
### Mobile (< md)
106+
### Mobile (< lg)
97107

98-
Header collapses to: logo + hamburger menu + auth control. Hamburger opens a sheet (right-side overlay) with all nav items stacked vertically — the content-cluster links, the About items under an "About" label, the GitHub link, and Volunteer last, mirroring the desktop order. Search is inside the sheet, not inline. The auth control stays in the header bar, outside the sheet.
108+
Header collapses to: logo + hamburger menu + auth control. Hamburger opens a sheet (right-side overlay) with all nav items stacked vertically — the content-cluster links, the About items under an "About" label, the Slack and GitHub links, and Volunteer last, mirroring the desktop order. Search is inside the sheet, not inline. The auth control stays in the header bar, outside the sheet.
99109

100110
Every item in the sheet closes the sheet when activated.
101111

@@ -200,7 +210,7 @@ It does not block initial paint waiting on `me`. Auth controls render skeletons
200210
- All dropdowns are keyboard-navigable
201211
- The mobile sheet traps focus while open and returns it to the trigger on close
202212
- The mobile sheet is a dialog with the accessible name "Menu"
203-
- At ≥ md the header exposes two navigation landmarks: "Primary navigation" (the content cluster) and "Utility" (GitHub, search, auth, Volunteer)
213+
- At ≥ lg the header exposes two navigation landmarks: "Primary navigation" (the content cluster) and "Utility" (Slack, GitHub, search, auth, Volunteer)
204214
- Every icon-only control carries an accessible name; controls with visible text use that text as their accessible name rather than duplicating it in a label
205215

206216
## Print

0 commit comments

Comments
 (0)