Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 22 additions & 0 deletions .cursor/rules/omd-design.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
description: Authoritative brand & UI design system. Read DESIGN.md before UI work.
globs:
- "**/*.tsx"
- "**/*.jsx"
- "**/*.vue"
- "**/*.svelte"
- "**/*.css"
- "**/*.scss"
- "**/tailwind.config.*"
- "**/components/**"
- "**/app/**/page.*"
alwaysApply: false
---

<!-- omd:start v=1 hash=f188a3882e27 -->
The authoritative design spec lives at `@DESIGN.md` (repo root). Open and read before generating/modifying UI.

Pending preference corrections: `@.omd/preferences.md`.

Precedence: DESIGN.md > preferences.md > framework defaults.
<!-- omd:end -->
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,7 @@ docs/superpowers/
# Turborepo
.turbo
**/.turbo

# Local AI design harness installation
/.agents/
/.codex/
18 changes: 18 additions & 0 deletions .omd/init-context.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"reference_id": "skyscanner",
"description": "Apply Skyscanner's desktop layout principles to every Solid Connection web POC page while preserving the existing color concept and component design.",
"mode": "inspired",
"requested_components": ["button", "input", "table", "card", "badge", "tabs", "dialog"],
"builder_url": "https://oh-my-design.kr/builder?step=preview&ref=skyscanner&cfg=c2t5c2Nhbm5lcnx8fHx8MHxidXR0b24saW5wdXQsdGFibGUsY2FyZCxiYWRnZSx0YWJzLGRpYWxvZw",
"delta_set": {
"axes": {
"density.shift": 0
},
"voiceHints": ["Preserve Solid Connection's existing Korean product voice"],
"matchedKeywords": ["desktop", "product", "search", "results", "filters"],
"warnings": [
"Skyscanner color, typography, logo, imagery, and branded component styling are explicitly excluded"
]
},
"bootstrapped_at": "2026-07-14T03:31:28.000Z"
}
21 changes: 21 additions & 0 deletions .omd/preferences.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
schema: omd.preferences/v1
design_md_hash_at_creation:
---

# Preference Log

## 2026-07-14T03:31:28.000Z — preserve-solid-connection-visual-identity

```omd-meta
id: pref_mrk3guqr_1401f1bd
timestamp: 2026-07-14T03:31:28.000Z
scope: visualTheme
signal: user-statement
confidence: explicit
status: pending
source_agent: codex
source_context: "feat/skyscanner-layout-poc"
```

Use Skyscanner only as a layout reference and preserve Solid Connection's existing color concept and component design.
9 changes: 9 additions & 0 deletions .omd/sync.lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"design_md_hash": "25bcf48d0657",
"targets": {
"CLAUDE.md": "672475c77b07",
"AGENTS.md": "be71c8d154c9",
".cursor/rules/omd-design.mdc": "f188a3882e27"
},
"updated_at": "2026-07-14T03:31:28.000Z"
}
8 changes: 8 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,11 @@
- Team workflow: `docs/development-workflow.md` and `CLAUDE.md`.
- LLM repository context: `llms.txt`.
- Bruno + codegen + React Query skill doc: `docs/skills/ai-bruno-codegen-react-query-skill.md`.

<!-- omd:start v=1 hash=be71c8d154c9 -->
## Design System (oh-my-design)

**Before any UI, styling, copy, or motion change, open and read `./DESIGN.md` in full.** It is the authoritative brand/design spec. Treat its tokens, voice, and component rules as binding unless the user overrides in chat.

If present, read `./.omd/preferences.md` — pending corrections not yet folded into DESIGN.md. Apply them; flag conflicts.
<!-- omd:end -->
11 changes: 11 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,14 @@ git commit -m "사용자 인증 기능을 추가했습니다. 이 기능은 JWT
- 관련된 설정 변경

여러 작업을 했다면 각각 별도의 커밋으로 분리합니다.

<!-- omd:start v=1 hash=672475c77b07 -->
# Design System (oh-my-design)

The authoritative brand & UI spec is **@./DESIGN.md**.
Read before any UI/styling/microcopy/motion work.

Preference log (pending corrections): @./.omd/preferences.md

Precedence: DESIGN.md > preferences.md > your defaults.
<!-- omd:end -->
113 changes: 113 additions & 0 deletions DESIGN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
---
omd: 0.1
brand: Solid Connection
bootstrapped_from: skyscanner
bootstrapped_at: 2026-07-14T03:31:28.000Z
---

# Solid Connection Desktop Layout System

This specification uses Skyscanner Backpack as a structural reference only. Solid Connection's visual identity is locked: existing colors, Pretendard typography, icons, copy style, component surfaces, and data behavior remain authoritative.

## 1. Visual Theme

- Keep the current calm, practical exchange-student product aesthetic.
- Do not copy Skyscanner colors, fonts, logo, illustrations, travel photography, or branded copy.
- Use Skyscanner only for desktop composition: a horizontal product navigation, a prominent task-entry area, centered wide content, filters beside results, and clear result density.
- Prefer functional product layouts over marketing-style hero sections.

## 2. Color Tokens

Colors are inherited from `apps/web/tailwind.config.ts` and must not be replaced by Skyscanner tokens.

| Role | Token | Value |
|---|---|---|
| Primary action | `primary` | `#5950F6` |
| Secondary action | `secondary` | `#4672EE` |
| Primary tint | `primary-100` | `#EFEEFF` |
| Secondary tint | `secondary-100` | `#E8EDFD` |
| Canvas | `k-0` | `#FFFFFF` |
| Page background | `k-50` | `#F5F5F5` |
| Border | `k-100` | `#DDDDDF` |
| Primary text | `k-900` | `#1A1F27` |
| Secondary text | `k-500` | `#76797D` |

Existing semantic accent colors remain allowed in their current domain-specific components. Do not introduce `#0062e3`, `#05203c`, Skyscanner teal, or Skyscanner error colors.

## 3. Typography

- Use the existing Pretendard variable font everywhere.
- Keep the existing `typo-*` utilities and their current values.
- Page title: `typo-bold-1` unless an existing page intentionally uses a smaller hierarchy.
- Section title: `typo-bold-4` or the existing local token.
- Body and metadata: preserve current `typo-medium-*` / `typo-regular-*` choices.
- Do not add Skyscanner Relative, Larken, viewport-scaled type, or negative letter spacing.

## 4. Desktop Layout

- Desktop begins at the repository's existing `md` breakpoint.
- Use one horizontal product navigation at the top. Keep it visible while scrolling.
- Center page content in a `1280px` maximum-width stage with `32-40px` side padding.
- Use a full-width task-entry band near the top of workflow-heavy pages.
- Results pages use a primary content column plus a `300-380px` filter/summary rail where useful.
- Dense card collections use responsive 2-4 column grids; long textual content remains a readable single column.
- Sticky rails sit below the top navigation, never underneath it.
- Auth and focused forms use a centered two-column composition only when the supporting panel adds real context.

## 5. Mobile Layout

- Preserve the current mobile UI and bottom navigation.
- Do not force desktop grids, horizontal product navigation, or wide containers onto mobile.
- Mobile remains single-column and keeps existing route-specific navigation and fixed actions.

## 6. Components

The builder-selected components are `button`, `input`, `table`, `card`, `badge`, `tabs`, and `dialog`. Their visual language remains the current Solid Connection implementation.

- Buttons: current primary/secondary colors, existing radius, existing heights, and current disabled behavior.
- Inputs: current border, radius, focus behavior, and error treatment.
- Cards: white surface, `k-100` border, existing 8px radius, subtle current shadows only.
- Badges and tabs: current primary tint/primary text treatment; do not import Skyscanner pill or navy-chip styling.
- Tables and rows: prioritize scanability and stable columns; preserve current semantic colors.
- Dialogs: preserve existing modal components and interaction behavior.

## 7. Navigation

- Desktop uses a white top bar with the Solid Connection cloud mark and wordmark.
- Primary sections remain 학교, 커뮤니티, 홈, 멘토, 마이.
- Active navigation uses existing primary colors and a subtle tinted surface.
- Mobile keeps the existing bottom navigation unchanged.

## 8. Responsive Behavior

| Width | Behavior |
|---|---|
| `<768px` | Existing mobile single-column UI |
| `768-1023px` | Compact desktop navigation, mostly single-column content |
| `1024-1279px` | Two-column workflows and wider card grids |
| `>=1280px` | Centered `1280px` stage, full result/filter composition |

- Minimum interactive target remains 44px where the current component supports it.
- Long Korean labels must wrap or truncate deliberately; they must not overlap adjacent content.
- No horizontal page overflow at 390px, 1024px, or 1440px.

## 9. States

- Preserve existing loading, empty, error, disabled, and selected states.
- Skeleton dimensions should match final card dimensions to avoid layout shift.
- Empty and error states stay inside the same desktop stage as successful content.
- Do not replace domain-specific messages with Skyscanner copy.

## 10. Motion

- Preserve current restrained transitions.
- Hover elevation and translation should remain subtle and must not shift surrounding layout.
- Honor `prefers-reduced-motion` for newly introduced motion.
- Do not add decorative motion, carousel movement, or Skyscanner signature animations.

## 11. Implementation Guardrails

- Layout changes may reorganize existing components but must not change API calls, route behavior, or business rules.
- Reuse existing components and Tailwind tokens before introducing new abstractions.
- A page is complete only after desktop and mobile browser verification, overflow checks, broken-image checks, and console review.
- Skyscanner is an inspiration source, not a visual clone. The final screen must still be immediately recognizable as Solid Connection.
6 changes: 3 additions & 3 deletions apps/university-web/src/app/university/(home)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ const UniversitySelectPage = () => {
</div>
</div>

<div className="hidden min-h-screen bg-k-50 px-8 py-8 md:block lg:px-10">
<div className="mx-auto max-w-6xl">
<div className="hidden desktop-page-shell md:block">
<div>
<header className="mb-8">
<p className="text-primary typo-sb-9">University catalog</p>
<h1 className="mt-2 text-k-900 typo-bold-1">소속 대학교 선택</h1>
Expand All @@ -74,7 +74,7 @@ const UniversitySelectPage = () => {
</div>
</section>

<aside className="sticky top-8 rounded-lg border border-k-100 bg-white p-6">
<aside className="desktop-sticky-panel rounded-lg border border-k-100 bg-white p-6">
<h2 className="text-k-900 typo-bold-4">이렇게 확인해요</h2>
<div className="mt-5 grid gap-3 text-k-700 typo-medium-2">
<div className="rounded-lg bg-k-50 px-4 py-3">소속 학교를 먼저 선택합니다.</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ const UniversityDetailDesktop = ({ university, koreanName, backHref = "/universi
<MapSection universityEnglishName={university.englishName} variant="desktop" />
</section>

<aside className="sticky top-8 space-y-5">
<aside className="desktop-sticky-panel space-y-5">
<DesktopUniversitySummary university={university} koreanName={koreanName} />
<DesktopLanguageSummary university={university} />
<DesktopRequirementSummary university={university} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ const UniversityListDesktopView = ({
setSelectedRegions,
}: UniversityListViewProps & { title: string }) => {
return (
<div className="hidden min-h-screen bg-k-50 px-8 py-8 md:block lg:px-10">
<div className="hidden desktop-page-shell md:block">
<header className="mb-8 flex items-end justify-between gap-6">
<div>
<p className="text-primary typo-sb-9">{title}</p>
Expand All @@ -184,7 +184,7 @@ const UniversityListDesktopView = ({
</header>

<div className="grid grid-cols-[300px_minmax(0,1fr)] items-start gap-8">
<aside className="sticky top-8 space-y-5">
<aside className="desktop-sticky-panel space-y-5">
<section className="rounded-lg border border-k-100 bg-white p-5">
<h2 className="mb-4 text-k-900 typo-bold-4">검색</h2>
<SearchBar value={searchText} onChange={setSearchText} homeUniversitySlug={homeUniversitySlug} />
Expand Down Expand Up @@ -221,7 +221,7 @@ const UniversityListMobileFallback = ({
);

const UniversityListDesktopFallback = ({ universities, homeUniversitySlug, title }: UniversityListContentProps) => (
<div className="hidden min-h-screen bg-k-50 px-8 py-8 md:block lg:px-10">
<div className="hidden desktop-page-shell md:block">
<header className="mb-8 flex items-end justify-between gap-6">
<div>
<p className="text-primary typo-sb-9">{title}</p>
Expand All @@ -237,7 +237,7 @@ const UniversityListDesktopFallback = ({ universities, homeUniversitySlug, title
</header>

<div className="grid grid-cols-[300px_minmax(0,1fr)] items-start gap-8">
<aside className="sticky top-8 space-y-5">
<aside className="desktop-sticky-panel space-y-5">
<section className="rounded-lg border border-k-100 bg-white p-5">
<h2 className="mb-4 text-k-900 typo-bold-4">검색</h2>
<SearchFallback homeUniversitySlug={homeUniversitySlug} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ const SearchPage = async ({ params }: PageProps) => {
<SearchPageContent homeUniversitySlug={homeUniversity} />
</main>
</div>
<div className="hidden min-h-screen bg-k-50 px-8 py-8 md:block lg:px-10">
<div className="mx-auto max-w-6xl">
<div className="hidden desktop-page-shell md:block">
<div>
<header className="mb-8">
<p className="text-primary typo-sb-9">{universityInfo.shortName} search</p>
<h1 className="mt-2 text-k-900 typo-bold-1">맞춤 파견 학교 찾기</h1>
Expand All @@ -88,7 +88,7 @@ const SearchPage = async ({ params }: PageProps) => {
</div>
</section>

<aside className="sticky top-8 rounded-lg border border-k-100 bg-white p-6">
<aside className="desktop-sticky-panel rounded-lg border border-k-100 bg-white p-6">
<h2 className="text-k-900 typo-bold-4">검색 기준</h2>
<div className="mt-5 grid gap-3 text-k-700 typo-medium-2">
<div className="rounded-lg bg-k-50 px-4 py-3">소속 학교: {universityInfo.name}</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const SearchDesktopContent = () => {
</div>
</section>

<aside className="sticky top-8 rounded-lg border border-k-100 bg-white p-6">
<aside className="desktop-sticky-panel rounded-lg border border-k-100 bg-white p-6">
<h2 className="text-k-900 typo-bold-4">소속 학교</h2>
<p className="mt-2 text-k-500 typo-medium-3">선택한 학교 기준으로 검색 결과가 제공됩니다.</p>
<div className="mt-5">
Expand Down
4 changes: 2 additions & 2 deletions apps/university-web/src/app/university/search/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ const Page = async () => {
<SearchClientContent />
</main>
</div>
<div className="hidden min-h-screen bg-k-50 px-8 py-8 md:block lg:px-10">
<div className="mx-auto max-w-6xl">
<div className="hidden desktop-page-shell md:block">
<div>
<header className="mb-8">
<p className="text-primary typo-sb-9">Search</p>
<h1 className="mt-2 text-k-900 typo-bold-1">맞춤 파견 학교 찾기</h1>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ type LayoutProps = {

const GlobalLayout = ({ children }: LayoutProps) => {
return (
<div className="min-h-screen bg-white md:bg-k-50 md:pl-[88px]">
<div className="min-h-screen bg-white md:bg-k-50">
<DesktopNavigation />
<div className="mx-auto mb-14 min-h-screen w-full min-w-app max-w-app bg-white pt-14 md:mx-0 md:mb-0 md:min-w-0 md:max-w-none md:pt-0">
<div className="mx-auto mb-14 min-h-screen w-full min-w-app max-w-app bg-white pt-14 md:mb-0 md:min-h-[calc(100vh-72px)] md:min-w-0 md:max-w-[1440px] md:bg-k-50 md:pt-0">
{children}
<BottomNavigation />
<AIInspectorFab />
Expand Down
Loading
Loading