Feat/islands - #554
Open
kake21 wants to merge 83 commits into
Open
Conversation
…ncy across user and admin components
…rors Renamed $base00-$base0F in _colors.scss to semantic surface-/ink-/accent- tokens and updated all call sites. Also cleaned up dead imports and variables left over from the nav-bar refactor (Menu, EditModeSwitch, itemsForMenu, BackButton, faArrowLeft) and fixed DesktopSideBar missing its required profile prop in layout.tsx. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Set --gap: 0.5rem in globals.scss and point $gap at var(--gap) instead
of a hardcoded 8px, so spacing can be adjusted at runtime via
document.documentElement.style.setProperty('--gap', ...). Since Sass
can't do compile-time math on a var(), every place that multiplied or
divided $gap directly is now wrapped in calc() so the arithmetic
happens in CSS instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Wrap every SCSS variable reference inside calc(...) in #{} (e.g.
calc(#{ohma.$gap} * 2)) per the project's IDE inspection preference.
Bare (non-calc) variable usages are left as-is.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Restyle SlideSidebar so each nav section header is a rounded card and its links are indented, darker rounded cards beneath it, matching the target admin design. Also tunes surface-base/surface-raised toward a cooler navy and sets --primary to a flat hex. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds max-width: calc(100dvh * 16 / 9) with auto margins to the root grid wrapper so the app fills the screen edge-to-edge on 16:9 displays but stays capped and centered on ultrawide monitors instead of stretching the whole layout across the full width. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
.wrapper used position: absolute + width: 100vw, breaking out of the root layout's .content grid area entirely (ignoring the nav/sidebar frame and the 16:9 max-width cap). Switch to flex: 1 so it fills its parent slot the same way error.module.scss and not-found.module.scss already do. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replace the full-bleed photo-hero tile (hover-zoom, text overlaid on the image) with an actual card: solid surface background, a 128x128 contained logo tile instead of a cropped background image, and the title laid out beside it. Reuses the boxShadow/rounding/surface-* tokens already established elsewhere (SlideSidebar, NavBar). Widens the committees grid columns to fit the new row layout. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Buttons now use rounding - gap instead of the full card radius, and the shared table mixin gets rounded corners, a raised header, and zebra/hover states pulled from the surface color tokens instead of the old color-mix + blur hacks.
Swap the Opp/Ned buttons on the flairs admin table for a drag handle, using framer-motion's Reorder for the spring-animated reordering. The table markup moves from <table> to a flex "fake table" since Framer's layout animations don't play well with table-row layout. Drop commits the new position by replaying increase/decreaseFlairRankAction the right number of times, then refreshes.
…tandard Fields now use the filled surface-raised background, rounding - gap corners, and primary-blue focus border established for buttons and tables. DateInput also gets color-scheme: dark so the native picker icon/popup stop rendering as a stark white box, and Checkbox swaps the unstyled native control for a custom appearance:none box with a CSS-drawn checkmark.
The root layout's mobile grid reserved a full nav-height row for a "subPageNavBar" area that nothing ever renders into — each page's SubPageNavBar lives inside its own content flow, not that named grid area. Dropping the empty row gives .content back ~72px of height it was silently losing on every mobile page.
…v layout Adds the user's top-ranked flair color as a border/frame accent on UserCard and the profile page, keeps the profile page's sub-nav pinned above the mobile nav bar at all times, and makes the profile card's content scroll within its fixed-height frame instead of the whole page. Also tweaks surface color tokens and swaps the secondary font to Roboto. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Migrates the flat legacy palette (primary/secondary/red/green/gray-*/ white/black) and duplicate aliases (tertiary, border, ink-base, ink-faint, text-sec, shadowColor) across the app to the surface/ink/ text/accent-* tokens so every surface actually responds to theme switching. Extends ThemeColors and all four theme presets (Standard, Light, Solarized, StjerneInnbygger) to drive the newly consolidated tokens, and fixes a couple of real bugs uncovered along the way: MobileNavBar and a few other components hardcoded white/black text on top of an already-themed background, which would have gone invisible under the Light theme. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ry button contrast Adds an /admin/component-test page rendering the UI kit (buttons, inputs, checkbox, slider, file input, progress bar) for visual/theme QA, linked from the admin sidebar under "Annet". SubmitButton's confirmation now teleports into the app's standard PopUp overlay instead of an inline swap, with explicit Ja/Nei actions. Fixes two bugs surfaced by that change: the trigger button defaulted to type="submit" and fired the real action immediately, and the real submit button ended up outside the <form>'s DOM once teleported so "Ja" did nothing — a hidden submit button now stays inside the form and is triggered via ref from the popup. Also fixes secondary buttons/links (Button, BorderButton, CmsLink, FileInput) using surface-base as their fill/border color, which is the same color as the page/card background they usually sit on and made them nearly invisible; they now use surface-hover instead. stateOfOmega's order number gets a centered, dramatic plaque treatment. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… viewport height: calc(100dvh - 60px) was a stale approximation of the app shell's chrome (nav bar, grid gaps, padding) and overflowed the visible content pane by ~28px. Derive the offset from the same nav-height/gap tokens the shell layout uses, with a mobile-breakpoint variant, and switch to min-height so the hero can grow instead of clipping if its content ever needs more space.
- Replaced static login button with dynamic user-dependent content. - Added `ProfilePicture` for logged-in users; styled logout state with a distinct button. - Refined navbar SCSS for conditional styling and responsive layouts. - Improved frontpage call-to-action buttons with refined styles and animations.
Float the login card on the left as a narrow vertical panel with the hooded crest logo up top, and use the frontpage hero photo as a large image panel on the right, instead of the small inverted hat icon.
…ign system Imports 40 presentational components into the "Ohma Design System" project on claude.ai/design, so the design agent builds with our real components instead of generic ones. projectNext is a Next.js app rather than a component library: there is no library build, and every component styles itself through *.module.scss, which esbuild cannot load. .design-sync/stage-pkg.mjs mechanically transforms the real source into a bundlable package — compiling each SCSS module to *.module.css via sass (a custom importer resolves the @/styles alias), rewriting only import specifiers, and shimming next/link and next/navigation. Component bodies are copied verbatim; nothing here reimplements a component. All four themes ship with it. stage-pkg.mjs reads theme.ts at build time and emits a :root[data-theme="<Name>"] block per theme, and the bundle re-exports themes / applyTheme / subscribeToTheme / getActiveTheme / ThemeName, so designs can switch palettes exactly as the app's own theme picker does. theme.ts stays the single source of truth — no palette values are copied. Contents: - scope.json which components sync, and their DS-pane grouping - stage-pkg.mjs the staging transform - vendor-inter.mjs vendors Inter, which _fonts.scss names as the site's body font but never ships (production currently falls back to system sans) - previews/ 40 authored preview compositions, all graded good - conventions.md the header prepended to the generated README, telling the design agent how to build with Ohma - NOTES.md environment quirks, decisions, and re-sync risks NOTES.md also records real defects surfaced while authoring the previews: unstyled native Select, FileInput's dark-on-black default, invisible Slider/TextInput 'secondary' variants, Checkbox losing its styling with children, BorderButton having no disabled state, SimpleTable's `links` prop emitting invalid HTML, and a palette mismatch between globals.scss and themes.Standard. These are reproduced faithfully in the cards rather than papered over. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ld idiom
FileInput, Select and ColorInput each rendered a bare native control while every
other input used the same floating-label field: surface-raised fill, 2px border
going accent-blue on focus, and a label that floats above the value. They are now
all built the same way — the real input stays focusable but visually hidden, and
the styled surface doubles as its label.
FileInput the chosen file name takes the place of the field value (multiple
files collapse to "N filer valgt"); useId() replaces a per-render
uuid that risked hydration mismatches
Select appearance:none plus the field treatment and a chevron; option
rows are styled so the native popup is dark rather than white
ColorInput the swatch stands in for the value with the hex beside it
All three gained `background="raised"`, and Select/FileInput gained the `white`
colour the other fields already had. No caller signatures changed.
Every field now measures exactly 56px with padding on an 8px grid (border 4 +
24 top + 8 bottom + 20px line box); every $gap multiplier across the eight
stylesheets is a whole number. DateInput needed its height pinned because the
native picker indicator inflated its content box, and the select-backed fields
needed explicit line boxes.
Also fixes a subtle bug shared by Dropdown: globals.scss sets `color` on the
universal selector, which matches a value <span> directly and beats the colour
inherited from its parent — so `color` variants silently did nothing wherever the
value was rendered in a span. Those rules now target the value itself.
SearchableDropdown was unaffected; it renders its value in an <input>.
Adds Color input and Selects sections to /admin/component-test, and updates the
design-system previews to match, including driving real file selection and colour
choices so the variant axes actually vary in the published cards.
.design-sync/stage-pkg.mjs now emits the .d.ts tree itself: it wipes its output
dir (types/ included) and the converter derives the component list from that
tree, so a forgotten tsc silently produced a 12-component bundle instead of 40.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The outline button existed twice over: as a BorderButton component used in only three places, and as a `borderBtn` mixin applied at ten more sites across seven page stylesheets. In every one of those, it was the secondary action sitting beside a filled primary — so the tier collapses into the filled secondary style we already had, and the design system is left with two action tiers instead of three. - deletes BorderButton (component + styles) and the `borderBtn` mixin - adds a `secondaryBtn` mixin holding the filled-secondary treatment, which Button.secondary now uses too instead of repeating it - converts all ten former mixin sites to `secondaryBtn`; CmsLink's explicit `.primary` variant becomes the filled primary tier instead - login: the submit is now `Button color="primary"` (it is the page's main action and was previously rendered as an outline, reading as secondary) and Feide sign-in is `color="secondary"` - AddParts: the CMS add-part toolbar becomes `color="secondary"` Section.module.scss styled `.readMore` identically in its `.blue` and `:not(.blue)` branches once the outline was gone, so that split is collapsed. Appearance changes, deliberately: every former outline button is now filled. That covers the login screen, the frontpage hero and section CTAs, CMS links, the news archive link and the applications/jobads/flairs secondary actions. Design system: BorderButton drops out of scope.json and its preview is deleted, taking the count from 40 to 39 components. conventions.md now tells the design agent there is no outline/ghost variant and not to invent one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Events were 2-up cards ~460px tall: a 200px cover image, a 150px body and
separate header/footer strips. They are now 96px full-width rows on the 8px
grid — thumbnail, date block, title + tags, then a right-aligned meta column,
with the attendance bar hugging the bottom edge. Roughly five times as many
events fit on a screen.
The cover image is kept as a square thumbnail rather than dropped, so the
CmsImage cover editing (canEdit) still works from the listings.
- EventCardListLayout becomes EventRowListLayout: a stacked single column at
every breakpoint instead of a 2-column grid
- LoggedInSection takes a `layout` prop; the frontpage events section opts into
'rows' so the rows are not squeezed into its 3-column card grid
- on mobile the row stays a row and the meta column drops out, rather than
reflowing back into a stacked card
- month abbreviations are Norwegian; they were full English month names in an
otherwise Norwegian UI
Also stops the row underlining itself on hover. The row is a single large
anchor, so globals' `a:hover { text-decoration: underline }` underlined every
text run inside it — date, title and all the meta. The background shift is the
hover affordance now.
Verified against the running dev app on /events and the frontpage, at desktop
and 430px. The archive listing shares EventsLandingLayout with /events but has
no seeded data to render.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Job ads were ImageCards in a 3-up grid, ~450px tall. They are now 96px full-width rows matching the event listings: thumbnail, application deadline, title + company/type, then a right-aligned meta column. The row geometry moved into a `listRow` mixin that both EventCard and JobAd include, so the two lists cannot drift apart. Consumers supply .thumb, .lead, .main, .meta and an optional .bar; EventCard was refactored onto those names. EventRowListLayout became the generic RowListLayout for the same reason. JobAd no longer goes through ImageCard. That component is still used by News, Articles and Education, so it is untouched — only the job-ad rendering changed. - deadline occupies the lead slot; applicationDeadline is nullable, so ads without one read "Løpende" rather than leaving the column blank - the description is dropped from the listing (there is no room in a row, and it is on the detail page); company and job type take its place - the "Inaktiv" badge moves into the meta column - frontpage Jobbannonser opts into the row layout, as the events section does - JobAdInactiveList's .loadingInfo loses `grid-column`, which is inert in a flex column Verified against the running dev app on /career/jobads and the frontpage; the inactive archive has no seeded data to render. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The logged-in landing page painted one surface-base panel behind everything and stacked its sections inside it. Each section is now its own island — a surface-base panel with rounded corners — and the page background shows through the gaps between them. - LoggedInSection gains a `span` prop: Nyheter and Bilder run full width, Hvad der hender and Jobbannonser pair up on a row once there is room - the hero image and the footer are rounded, and the frontpage wrapper is a flex column with a $gap step, so the hero, the content and the footer are each separated by one grid unit - content max-width goes 900px -> 1200px, and the frontpage drops its own side padding; what remains at the sides is pagePadding centring, not padding - the rule under each section title is gone and "Les mer" is now a pill button matching the "Ny +" header item (same height, radius, surface and hover) - social icons are gone from the logged-in page; they remain in the footer listRow switches from a viewport media query to a container query. Rows now sit both full width and inside half-width islands, and a viewport query left the title about 50px in a narrow island on a wide screen — the meta column now gives way based on the space the row actually has. Also defines .footer in page.module.scss. LoggedIn and LoggedOut both referenced styles.footer, which was never declared, so that wrapper rendered with class="undefined" on both frontpages. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Hvad der hender and Jobbannonser share a row on wide screens, but their
flex-basis of 50% kept them side by side all the way down, splitting a phone
screen in two. They now take the full width below the mobile breakpoint.
On phone-width rows the date no longer sits in a column beside the title —
there is not enough horizontal room for it there. The row becomes a grid: the
thumbnail keeps the left edge at full height, and the date runs horizontally
above the title in the space that remains.
Fixes the container-query setup underneath both. `container-type` was on the
row itself, but a container query can only style descendants of its container,
never the container element — so rules targeting the row were silently dead
(the earlier `> .meta { display: none }` worked only because it targets a
child). The container moves to RowListLayout, the list wrapping the rows, which
has the same width and the right semantics. All four row lists get it: the
frontpage islands, the events landing page, the job-ad listing and its archive.
Measured against the compiled CSS in headless chromium: at 1152px and 652px the
row stays flex with a vertical date and its meta column; at 382px it is grid,
the date is horizontal and above the title, both share a left edge clear of the
96px thumbnail, and the row is still 96px tall.
Also includes: "Les mer" becomes "Se flere", and ImageCard's image corners use
$rounding rather than $cardRounding.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The two component defects the previous sync documented are fixed: Select is no longer an unstyled native control and FileInput's default `black` variant no longer renders dark-on-black. Verified against the freshly built CSS, so the "prefer Dropdown" steer and the FileInput warning are removed from the design agent's header and struck through in the notes. Also record the 56px field shell across all ten fields, correct the stale `buildCmd` (stage-pkg.mjs emits the .d.ts tree itself), and drop the preview count to 39 now that BorderButton is retired. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ESLint was walking .design-sync/.cache/ and ds-bundle/, neither of which is
tracked, which buried the run under 594 errors from generated code. Ignore
both, and fix the 16 real errors in the tracked previews:
- Previews typed children as React.ReactNode without importing React; use a
`import type { ReactNode }` instead, as the components in src/ do.
- SimpleTable inlined a styled span per row, pushing three lines past max-len;
pull Name and Status out so the table body reads as data.
- Dropzone's WithFiles copied the generated files into a second useState via
an effect, cascading renders. usePngFiles now hands back its own state pair.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Extract ArchiveLink as a shared HeaderItems component and use it on jobads, events and news instead of each page styling its own link. Move the events page onto PageWrapper/RowListLayout to match jobads and news, and give LoggedInSection an empty-state placeholder so paired islands keep their height when a list has nothing to show. EventCard drops its inline CMS cover-image editing (now a plain Image) since it no longer needs the canEdit auth check threaded through every caller.
…on edit mode Matches the frontpage islands treatment: each interest group and the intro text paint their own surface-base panel with the page background showing through the gaps, and the settings popup only appears in edit mode instead of an always-visible icon. ArticleSection's image sizing needed a fix to keep working once the group card stopped giving it a fixed-height row to size against.
…der spacing Retire the bespoke EventsLandingLayout in favor of PageWrapper, the same component the events, news, and jobads pages already use, so the archive page's header, title, and card list match the main events page instead of diverging from it. Also gives the jobads header buttons the same gapped wrapper the events/news headers already use, fixing them rendering flush against each other. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CLrGtHhr5s8aRSCkxkJijn
Give /users PageWrapper's fillHeight mode so the header and filters stay pinned while only the table body scrolls. fillHeight previously hard-coded a transparent background for its one existing consumer (the user settings layout); decouple that so fillHeight alone keeps the normal solid panel, and move the transparent look behind the existing transparent prop, applied explicitly where it's still wanted. Also tighten every table's corner rounding from $rounding to $gap. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CLrGtHhr5s8aRSCkxkJijn
The mobile sidebar had open/closed state in JS but no matching CSS, so it always sat as a fixed 40dvh block over the page with no way to dismiss it. It's now a proper off-canvas drawer with a toggle button, backdrop, and click-outside/Escape/navigation to close. Also fixes a stacking-context bug: .slideBar's unconditional position: sticky trapped the drawer's fixed-position elements below the page content regardless of z-index, so the toggle button couldn't be clicked on sub-pages. Scoped position: sticky to desktop only. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CLrGtHhr5s8aRSCkxkJijn
Standardize margins for `.registrationButton` and refine layout gap/padding using `$gap` for consistency across event pages.
…n dropdown Add `overflow-x: hidden` to menu container and update link styles to use consistent width and full alignment.
…der spacing Introduce a styled back button for the news archive header consistent with the shared layout. Fix mobile spacing for job ad headers and adjust margin behavior for better layout alignment.
…e layouts Expand `ImageCard` max-width and refine height calculations with `calc()` for consistent scaling. Add mobile-specific margin and padding updates across components and adjust styling for better layout flexibility.
…height Restructures the committee layout into a grid with a scrollable main area, lays the logo and cover image side by side instead of stacked, and stretches the page content to fill the remaining viewport height. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DdkD6352w1jLe6TVj5KUnn
kake21
marked this pull request as ready for review
August 23, 2026 09:41
# Conflicts: # src/app/_components/NavBar/NavBar.tsx # src/app/committees/[shortName]/layout.tsx # src/app/image-collections/MakeNewCollection.tsx # src/app/layout.tsx # src/app/news/page.tsx # src/app/users/[username]/(user-admin)/theme/ThemeForm.tsx # standard_store/logo_simple.svg # standard_store/logo_white.svg # standard_store/magisk_hatt.svg # standard_store/omega_logo_white.svg # standard_store/pwa.svg # standard_store/vevcom_logo.svg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Major style overhaul of projectNext.
Extends #526 and #514.
Continues work on #357 and #401.
Implements hamburger menu from #7 issue.
Waiting on SVG support from CMS to color logos in mobile navbar.
PageTitleProvider(mounted once inlayout.tsx) holds the current title; each pagerenders
<PageTitleSetter title="..." />, which pushes the title up via a plainuseEffect(set on mount/change, clear on unmount) and
NavBarTitlereads it back out to render in the nav bar. Adopted across ~15 pages(auth, admin, users, events, committees, images, error/not-found, etc.) to avoid prop-drilling a title throug
h every layout.
Images showing the proposed stylechange:
Logged in main page desktop:

Profile page desktop:

Logged in main page mobile:

Pofile page mobile:
