Skip to main content

Project Page - Category Pills and Reset Button

Updated Apr 12, 2026 ·

Overview​

On larger screens the projects page shows a two-column layout with a categories sidebar on the left for filtering.

On screen sizes 897px and below, the original design hid the entire sidebar (see Step 6 in Designing the Project Page), which meant category filtering was unavailable on mobile.

This page documents two follow-up changes:

  1. Keep category filtering on mobile screen sizes.
  2. Add a Clear All button that resets all active filters.

After the fix:

On large screens (light and dark mode):

On mobile screens (light and dark mode):

Files Modified​

FileChange
src/pages/projects.tsxAlways-visible Clear All button (always enabled)
src/components/projects/Project.module.scssMobile pill styles (@media (max-width: 897px)), .clearAllButton, dark mode overrides

Mobile Pill Buttons​

Instead of hiding the sidebar, I converted the category list into pill-shaped buttons that is laid our in a row. The buttons also wrap across the top of the page.

Layout Change​

The existing media query previously set the left column to display: none. This has been changed to keep it visible and switches the layout to a single column with two rows:

  • Row 1: The pill buttons
  • Row 2: The project cards
@media (max-width: 897px) {
.pageContainer {
grid-template-columns: 1fr !important;
grid-template-rows: auto 1fr;

.leftColumn {
display: flex; /* was: display: none */
}
...
}
}

Category List​

The categoriesList switches from a vertical flex column to a horizontal flex row with wrapping enabled, so categories flow side by side and wrap to the next line when the row fills up.

.categoriesList {
flex-direction: row;
flex-wrap: wrap;
gap: 12px;
}

Hiding the Actual Checkbox​

The native checkbox input is visually hidden but kept in the DOM so the existing filtering logic (controlled React state) continues to work unchanged.

.categoryToggle {
position: absolute;
opacity: 0;
pointer-events: none;
}

Pill Label​

The label associated with each checkbox is styled to look like a pill button. Clicking the label still toggles the hidden checkbox, so no changes are needed in the TSX filtering logic.

.categoryLabel {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 8px 14px;
font-size: 11px;
border: 1px solid #d2d7e2;
border-radius: 999px;
background-color: #f4f6fb;
color: #4c566f;
cursor: pointer;
transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

.categoryToggle:checked + .categoryLabel {
background-color: #40939b;
border-color: #40939b;
color: #ffffff;
}

Dark Mode​

Also added dark-mode settings for the same breakpoint (897px and below).

[data-theme="dark"] .projectsPage {
@media (max-width: 897px) {
.categoryLabel {
background-color: #163346;
border-color: #2b5267;
color: #d9e3e4;
}

.categoryToggle:checked + .categoryLabel {
background-color: #40939b;
border-color: #40939b;
color: #ffffff;
}
}
}

Categories Header​

The CATEGORIES heading is hidden on mobile since the pill buttons are self-explanatory in this context.

.categoriesHeader {
display: none;
}

Clear All Button​

The "Clear All" button will allow users to reset all active category filters in a single click, which will display all the projects again. This button is visible on all screen sizes.

Implementation​

The "Clearn All" button is always rendered and always enabled. Clicking it calls setSelectedCategories([]), which clears the state and shows all projects again.

<button
className={styles.clearAllButton}
onClick={() => setSelectedCategories([])}
>
Clear All
</button>

Styling​

The button is intentionally plain (meaning it's not a pill button like the categories),so it reads as a utility action rather than a filter toggle. The underline indicates it's clickable, and a hover color change provides interactivity feedback.

.clearAllButton {
margin-top: 16px;
background: none;
border: none;
padding: 0;
font-size: 0.8rem;
color: #696f83;
cursor: pointer;
text-decoration: underline;
text-underline-offset: 2px;
display: block;

&:hover {
color: #40939b;
}
}