Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
75eace6
docs: design tailwind migration
tyler-dane Jun 18, 2026
86de2cf
test(web): characterize theme tokens
tyler-dane Jun 18, 2026
6cccbd7
refactor(web): migrate shared primitives to tailwind
tyler-dane Jun 18, 2026
cb39b4a
refactor(web): migrate shared composites to tailwind
tyler-dane Jun 18, 2026
f562653
refactor(web): migrate event forms to tailwind
tyler-dane Jun 18, 2026
17966b9
refactor(web): migrate planner sidebar to tailwind
tyler-dane Jun 18, 2026
50e638d
refactor(web): migrate calendar grid to tailwind
tyler-dane Jun 18, 2026
4f15459
refactor(web): remove styled theme provider
tyler-dane Jun 18, 2026
809861e
refactor(web): remove styled components dependency
tyler-dane Jun 18, 2026
59091ce
fix(web): restore role=form on event forms
tyler-dane Jun 18, 2026
9c4c405
docs: remove styled-components references from cursorrules
tyler-dane Jun 18, 2026
c3ddaac
docs: remove styled-components migration design spec
tyler-dane Jun 19, 2026
1772adc
chore: cleanup
tyler-dane Jun 19, 2026
5e29480
Merge branch 'main' into refactor/styled-components-to-tw
tyler-dane Jun 19, 2026
d578f9d
test: delete temp styled-components tests
tyler-dane Jun 19, 2026
7b53f87
refactor(web): remove dead reminder CSS utilities
tyler-dane Jun 19, 2026
9bd767e
refactor(web): inline one-off layout utilities
tyler-dane Jun 19, 2026
ff6e268
refactor(web): inline variant-based someday utilities
tyler-dane Jun 19, 2026
3620cdd
refactor(web): inline calendar grid layout utilities
tyler-dane Jun 19, 2026
09073f9
docs: encode the inline-first c-* decision test in styling rules
tyler-dane Jun 19, 2026
1e9587c
refactor(web): inline rarely-reused c-* utilities
tyler-dane Jun 19, 2026
05693f7
refactor: rename 'compass-scroll' to 'c-scroll'
tyler-dane Jun 19, 2026
45d1d25
refactor(web): inline c-not-found-back-button utility
tyler-dane Jun 19, 2026
ed76cb8
fix(web): restore timepicker dropdown styling
tyler-dane Jun 19, 2026
40b76a1
style: fix datepicker widgets
tyler-dane Jun 19, 2026
c5ab716
docs(web): design event form actions row
tyler-dane Jun 20, 2026
d5bbe53
fix(web): align event descriptions with actions
tyler-dane Jun 20, 2026
bdd323c
fix(web): align event titles with actions
tyler-dane Jun 20, 2026
faab604
docs(web): design inline event form styles
tyler-dane Jun 20, 2026
565e166
style: simplify form styles
tyler-dane Jun 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .cursorrules/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ You are an expert full-stack developer working on Compass, a calendar applicatio

This is a monorepo using Bun workspaces with the following packages:

- `@compass/web` - React/TypeScript frontend with Redux, styled-components, webpack
- `@compass/web` - React/TypeScript frontend with Redux, Tailwind CSS
- `@compass/backend` - Express.js REST API with MongoDB, Google Calendar sync, WebSocket support
- `@compass/core` - Shared utilities, types, and business logic
- `@compass/scripts` - CLI tools for building, database operations, user management
Expand Down Expand Up @@ -42,7 +42,7 @@ This directory contains focused rules for different aspects of development:
```
packages/
├── backend/src/ # Express.js API, MongoDB, Google Calendar sync
├── web/src/ # React frontend, Redux state, styled-components
├── web/src/ # React frontend, Redux state, Tailwind CSS
├── core/src/ # Shared utilities, types, business logic
└── scripts/src/ # CLI tools for builds and operations
```
Expand Down
42 changes: 39 additions & 3 deletions .cursorrules/styling.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,45 @@ The pattern is: CSS variable `--color-{category}-{name}` → Tailwind class `{ca

### Styling Approach

- Use Tailwind utility classes directly in JSX
- For complex styles, use styled-components (already configured)
- Follow existing patterns in `packages/web/src/components/`
Use a hybrid of inline utilities and `c-*` recipes. **Inline is the default.**

The deciding question for extracting a `c-*` recipe is *"can this be expressed
inline at all?"* — **not** "is it long?" or "is it used twice?". Reach for a
`c-*` recipe (Tailwind v4 `@utility c-...` in `packages/web/src/index.css`) only
when inline utilities genuinely cannot do the job, i.e. it has any of:

1. **Third-party descendant selectors** you don't control — `.react-datepicker__*`,
`.timepicker__*` (e.g. `c-date-picker`, `c-time-picker`).
2. **Pseudo-elements / vendor scrollbars** — `::before`, `::after`,
`::-webkit-scrollbar` (e.g. `compass-scroll`). Prefer the Tailwind `before:` /
`after:` variants inline first; only extract if that gets unreadable.
3. **Keyframe-driven animation bundles** (e.g. `c-loader-spinner`).
4. **Genuine reuse** across components, or a coherent variant set
(e.g. `c-button*`, `c-event-form`).

Otherwise, **inline it**:

- **Inline Tailwind utilities** for one-off layout and state, in JSX directly
(e.g. `className="mb-2.5 items-center justify-end gap-[30px]"`). Use `data-[…]:`
and `hover:` variants instead of `&[data-…]`/`&:hover` recipe blocks. Arbitrary
values are fine for runtime vars: `grid-cols-[repeat(7,minmax(80px,1fr))]`,
`w-[calc(100%_-_50px)]`.
- If a recipe exists only to reach into children via `& .child` / `& > *`, put
the utilities **on the child elements** instead and delete the descendant rule.
- **Do not** create one-off `c-*` recipes named after their implementation
(`c-week-columns`, `c-calendar-grid-rows`); that just recreates a global CSS
layer. Inline them.
- **Semantic CSS-variable tokens** for all theme-dependent colors (see above),
so a future `[data-theme="light"]` rollout needs no component changes.
- **Inline CSS custom properties** only for runtime values that cannot be known
at build time — event colors, positions, dynamic grid counts (e.g.
`style={{ "--event-form-bg": color }}`).

Follow existing patterns in `packages/web/src/components/` and the recipes in
`packages/web/src/index.css`.

Preserve semantic attributes (`role`, `aria-*`, `title`) when restyling — they
are load-bearing for assistive tech and e2e selectors, not presentation.

## Module Imports

Expand Down
2 changes: 1 addition & 1 deletion CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ during Import or Public watch notification handling.
- Treat recurring event changes as changes to a **Recurring Series**, not just
isolated event rows.
- Do not use **Someday Event** and **Task** interchangeably.
- Do not describe the frontend as Tailwind-only or styled-components-only.
- Frontend styling uses Tailwind utilities and semantic runtime CSS variables.
- Do not describe local self-hosting as proving continuous Google sync unless a
public HTTPS webhook path has been configured and verified.
- When changing a shared event, sync, API, or error contract, keep the web,
Expand Down
9 changes: 0 additions & 9 deletions babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,4 @@ module.exports = {
["@babel/preset-env", { targets: { node: "current" } }],
"@babel/preset-typescript",
],
plugins: [
[
"babel-plugin-styled-components",
{
meaninglessFileNames: ["index", "styled"],
pure: true,
},
],
],
};
44 changes: 3 additions & 41 deletions bun.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/frontend/frontend-runtime-flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,10 @@ Important consequence:

The web app currently uses two styling systems in parallel:

- longstanding `styled-components` for much of the existing UI
- Tailwind utilities for component styling
- Tailwind v4 utilities and semantic theme tokens from `packages/web/src/index.css` for newer or migrated surfaces

Do not describe the frontend as Tailwind-only or styled-components-only. Follow the local pattern of the area you are editing unless the change is explicitly migrating that area.
Use the existing `c-*` component utility convention and semantic colors from `packages/web/src/index.css`. Runtime theme values belong in `--compass-*` CSS variables so alternate themes can override values without rebuilding component styles.

## Day Task Drag Handle Positioning

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Event Form Title Actions Row

## Problem

The regular and Someday event forms render their actions menu separately from
the title. This separates controls that should share the first form row.

## Design

Add a small shared `TitleActionsRow` component for the two event forms. It
renders the title as the first child and the actions menu as the second child.
The row uses flex layout, allows the title to consume the remaining width, and
keeps the actions menu aligned at the right edge.

Both `EventForm` and `SomedayEventForm` wrap their existing title and
action-menu components with the shared row. Descriptions remain standalone.
Existing event behavior, callbacks, menu contents, field styling, and keyboard
handling remain unchanged.

## Testing

Add focused regression coverage proving that the shared row preserves the
title-first DOM order and right-side actions layout. Cover both regular and
Someday form integration so either form cannot accidentally restore the
standalone menu row. Run the focused web tests, type checking, lint, and the
React diff audit.

## Scope

This change only repairs the title/actions layout. It does not redesign other
form rows or alter menu and description behavior.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Inline Event Form Styles

## Problem

The global `c-event-form`, `c-event-form-title`, and
`c-event-form-description` utilities each style only the regular and Someday
event forms. They hide straightforward Tailwind semantics in `index.css` and
make local form styling harder to read.

## Design

Delete the three global utilities and inline their Tailwind classes at both
form call sites. Preserve the current visual behavior, including the dynamic
form background and shadow tokens, title typography and hover state, and
description transitions and hover treatment.

Use semantic Tailwind colors where available. Keep arbitrary CSS-variable
values only where the existing design token has no direct utility. Replace the
description's calculated width with `w-full`, which matches its role as the
standalone description field.

Do not add shared class constants or another wrapper component. The explicit
local class strings are the purpose of this refactor. Preserve the existing
uncommitted removal of `role="form"` from `EventForm`.

## Testing

Update the form Tailwind source test first so it requires the three global
utilities to be absent and the relevant local classes to exist in both forms.
Run the focused source test, the full web suite, type checking, lint, and React
Doctor.

## Scope

This refactor changes style ownership only. It does not alter form behavior,
field order, actions, or event data handling.
2 changes: 0 additions & 2 deletions packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
"redux": "^4.1.1",
"redux-saga": "^1.1.3",
"rxjs": "^7.8.0",
"styled-components": "^5.3.1",
"supertokens-web-js": "^0.16.0"
},
"devDependencies": {
Expand All @@ -47,7 +46,6 @@
"@types/react": "^18.0.8",
"@types/react-datepicker": "^4.1.7",
"@types/react-dom": "^18.0.3",
"@types/styled-components": "^5.1.14",
"bun-types": "^1.2.18",
"fake-indexeddb": "^6.2.5",
"jsdom": "^26.1.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@ import {
gridHoverColorByPriority,
} from "@web/common/styles/theme.util";
import { type Schema_GridEvent } from "@web/common/types/web.event.types";
import { Flex } from "@web/components/Flex";
import { AlignItems, FlexDirections } from "@web/components/Flex/styled";
import { AlignItems, Flex, FlexDirections } from "@web/components/Flex/Flex";
import { SpaceCharacter } from "@web/components/SpaceCharacter";
import { Text } from "@web/components/Text";
import { Text } from "@web/components/Text/Text";

export interface CalendarAllDayEventCardProps {
event: Schema_GridEvent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
type ReactNode,
type RefCallback,
} from "react";
import styled from "styled-components";
import {
CALENDAR_EVENT_WIDTH_MINIMUM,
CALENDAR_GRID_MARGIN_LEFT,
Expand All @@ -16,7 +15,8 @@ import {
ID_ALLDAY_COLUMNS,
ID_GRID_ALLDAY_ROW,
} from "@web/common/constants/web.constants";
import { Flex } from "@web/components/Flex";
import { type CSSVariables } from "@web/common/styles/css.types";
import { Flex } from "@web/components/Flex/Flex";

interface CalendarAllDayRowProps {
allDayColumnsRef: RefCallback<HTMLDivElement>;
Expand All @@ -38,53 +38,6 @@ const getAllDayRowHeight = (gridOffsetTopPx: number) => {
return `${gridRowHeight} / ${interval}`;
};

const StyledAllDayColumns = styled.div<{ $visibleDateCount: number }>`
display: grid;
grid-template-columns: ${({ $visibleDateCount }) =>
`repeat(${$visibleDateCount}, minmax(${CALENDAR_EVENT_WIDTH_MINIMUM}px, 1fr))`};
height: 100%;
left: ${CALENDAR_GRID_MARGIN_LEFT}px;
position: absolute;
top: 0;
width: calc(100% - ${CALENDAR_GRID_MARGIN_LEFT}px);

&::before {
background: ${({ theme }) => theme.color.gridLine.primary};
bottom: 0;
content: "";
height: 2px;
left: 0;
pointer-events: none;
position: absolute;
right: 0;
}
`;

const StyledAllDayRow = styled(Flex)<{
$gridOffsetTopPx: number;
$rowsCount: number;
}>`
flex-shrink: 0;
height: ${({ $gridOffsetTopPx, $rowsCount }) => {
const allDayRowHeight = getAllDayRowHeight($gridOffsetTopPx);

return `calc(${allDayRowHeight} * 2 + ${
$rowsCount * 2 || 1
} * ${allDayRowHeight})`;
}};
position: relative;
width: 100%;
`;

const StyledDateColumn = styled.div`
border-left: ${({ theme }) => `1px solid ${theme.color.gridLine.primary}`};
box-sizing: border-box;
display: block;
height: 100%;
min-width: ${CALENDAR_EVENT_WIDTH_MINIMUM}px;
position: relative;
`;

export const CalendarAllDayRow: FC<CalendarAllDayRowProps> = ({
allDayColumnsRef,
allDayRowRef,
Expand All @@ -96,28 +49,38 @@ export const CalendarAllDayRow: FC<CalendarAllDayRowProps> = ({
rowId = ID_GRID_ALLDAY_ROW,
visibleDates,
}) => (
<StyledAllDayRow
$gridOffsetTopPx={gridOffsetTopPx}
$rowsCount={rowsCount}
<Flex
className="relative w-full shrink-0"
aria-label="All-day events"
id={rowId}
ref={allDayRowRef}
role="region"
onMouseDown={onMouseDown}
style={{
height: `calc(${getAllDayRowHeight(gridOffsetTopPx)} * 2 + ${rowsCount * 2 || 1} * ${getAllDayRowHeight(gridOffsetTopPx)})`,
}}
>
<StyledAllDayColumns
$visibleDateCount={visibleDates.length}
<div
className="absolute top-0 left-[var(--calendar-grid-margin-left)] grid h-full w-[calc(100%_-_var(--calendar-grid-margin-left))] grid-cols-[repeat(var(--calendar-column-count),minmax(var(--calendar-column-min-width),1fr))] before:pointer-events-none before:absolute before:inset-x-0 before:bottom-0 before:h-0.5 before:bg-grid-line-primary before:content-['']"
id={columnsId}
ref={allDayColumnsRef}
style={
{
"--calendar-column-count": visibleDates.length,
"--calendar-column-min-width": `${CALENDAR_EVENT_WIDTH_MINIMUM}px`,
"--calendar-grid-margin-left": `${CALENDAR_GRID_MARGIN_LEFT}px`,
} as CSSVariables
}
>
{visibleDates.map(({ date, key }) => (
<StyledDateColumn
<div
className="relative box-border block h-full min-w-[var(--calendar-column-min-width)] border-grid-line-primary border-l"
aria-label={date.format("dddd, MMMM D, YYYY")}
key={key}
role="columnheader"
/>
))}
</StyledAllDayColumns>
</div>
{eventsLayer}
</StyledAllDayRow>
</Flex>
);
Loading
Loading