Skip to content

Feat/islands - #554

Open
kake21 wants to merge 83 commits into
mainfrom
feat/islands
Open

Feat/islands#554
kake21 wants to merge 83 commits into
mainfrom
feat/islands

Conversation

@kake21

@kake21 kake21 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

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.

  • Standardizes core UI components (Button, TextInput, DateInput, Checkbox, Textarea, NumberInput, SubmitButton) onto a consistent card/gap/rounding system, and adds new Dropdown/SearchableDropdown components used to rebuild the admin user list (sortable, standardized).
  • Various admin page redesigns (sidebar as nested cards, /admin/dots, flairs with animated drag-to-reorder, a new component test page) and smaller fixes (CommitteeCard, HeaderItemPopUp, PopUp, jobads/events zoning, omegaquote bubble tail, edit-mode icon visibility, frontpage preview link).
  • PageTitle context: PageTitleProvider (mounted once in layout.tsx) holds the current title; each page
    renders <PageTitleSetter title="..." />, which pushes the title up via a plain useEffect (set on mount/ch
    ange, clear on unmount) and NavBarTitle reads 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.
  • Changed the colors to a base16 inspired layout.

Images showing the proposed stylechange:

Logged in main page desktop:
image

Profile page desktop:
image

Logged in main page mobile:
Screen Shot 2026-08-05 at 17 46 52

Pofile page mobile:
Screen Shot 2026-08-05 at 17 47 53

kake21 and others added 30 commits May 28, 2026 04:57
…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>
kake21 and others added 28 commits August 10, 2026 21:04
… 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
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants