Feat/islands - #554
Draft
kake21 wants to merge 166 commits into
Draft
Conversation
…geUploadData for reuse
…admin and user pages
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ncy across user and admin components
Swap the mount/localStorage useEffect+setState pattern in NavTooltip and ThemeForm for useSyncExternalStore, which avoids react-hooks/set-state-in-effect without changing behavior. Also wraps several lines that exceeded max-len. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…olumns Switches the footer from a fixed CSS grid to flex-wrap so sections keep their natural content width and drop to a new row as whole blocks instead of shrinking narrow enough to break emails/phone numbers mid-word. Kontakt and Adresse are grouped in a shared .infoGroup flex item so they always move together rather than splitting across rows independently. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Uses standard scrollbar-color/scrollbar-width plus WebKit pseudo-elements for Chrome, styled with the existing theme colors. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…d alignment - Updated ReportButton to use dynamic spacing variables and improved hover effect. - Resized NavBar action buttons and added flex alignment for better UI consistency.
…components - Introduced expand/collapse functionality to DesktopSideBar with transition effects. - Updated EditModeNavIcon and AdminNav to support expanded state for better UX. - Refactored and modularized styles for improved alignment and responsiveness. - Adjusted layout to handle expanded sidebar properly with grid-template changes.
The avatar button now links straight to /users/me (or /login when signed out) instead of opening a click-triggered dropdown. The profile page already surfaces settings, Omega-ID, account and logout, so the popup was redundant. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The Link was nested inside StandardImageServer's overlay slot, which sizes itself to the tightly-fit image box (~30x27px) rather than the visible 48x48 logo button, so only clicks on the image itself worked. Wrapping the Link around the whole logoWrapper instead makes the full button clickable. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Adopt the shared PageWrapper component for the page title, header row and min-height so the page fills the content area instead of collapsing to its content size. - Swap the bare PopUp for AddHeaderItemPopUp so "new collection" gets the app's standard styled round button instead of an unstyled plus. - Style the Bildealbum/Spesialsamlinger mode-switch buttons with the ohma.btn mixin instead of relying on unstyled native buttons with only opacity toggling. - Switch the collection grid from a fixed 4-column CSS grid with three manual breakpoints to flex-wrap, which reflows naturally. - Move the "only administrated collections" checkbox inline with the mode-switch buttons instead of stacking it on its own line. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Visible <input> elements should go through @/components/UI (TextInput, Checkbox, NumberInput, etc.) instead of being written raw, so they get consistent styling and behavior - e.g. Checkbox's hidden "field present" input, which keeps unchecked boxes submitting correctly. type="hidden" inputs are exempt since they're just plain form data carriers with no component equivalent, and the UI folder itself is exempt since that's where these primitives are implemented. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The current-image thumbnail shown when changing a CMS image used object-fit: contain, leaving visible background gaps around non-square images. Switch to cover so it fills its clip area like the rest of the app's image thumbnails. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…out !important EventCard's images were overflowing their 420x200 card box and being badly cropped, because CmsImage's wrapper divs sized themselves off the image's declared width (a fixed pixel value) rather than their container. The declared width is Image.tsx's `width` prop, applied as an inline style on its wrapper div. Inline styles can only be overridden with !important, which is why ~10 components across the app were already carrying `width: 100% !important` just to make an image responsive. Image now sets that width via a `--image-width` custom property instead, so it renders through a normal stylesheet rule consumers can override with ordinary CSS - no !important required. Cleaned up the now-unnecessary !important from CommitteeCard, ImageCard, ImagePanelImage, ImageDisplay, CollectionCard, OmbulCover, and Article's cover image, and applied the same depth-agnostic wrapper-div fix used for EventCard to Article's cover image, which had the same latent bug for the no-image-chosen-yet fallback. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Reproduced an intermittent "Maximum update depth exceeded" crash on article pages, pointing at EditModeNavIcon/EditModeSwitch. Root cause traced to useEditMode: its registration effect depended on the whole authResult (and authorizer) object, but useAuthorizer constructs a new AuthResult on every render (authorizer.auth(...) is unmemoized), and several callers (e.g. EditableTextField) construct their authorizer object inline as well. That made the effect's dependency array different on every render of every component using this hook, so it re-ran constantly instead of only when authorization actually changed - amplified further since EditModeContext's Provider value is a fresh object each render too, cascading the re-renders to every subscriber. Depend on the primitive authResult.authorized instead of the object, which only actually changes when authorization status changes. Also hoisted EditableTextField's inline RequireNothing authorizer to a module constant, since it takes no dynamic args and was being reconstructed on every render for no reason. Reproduced repeatedly before the fix (~1 in 3-4 page loads); toggled edit mode on/off rapidly several times after the fix with no repeat. Given the intermittency, treat this as a strong fix rather than a 100%-confirmed one. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tructure - Moved social icons into reusable `socials` section and updated related styling for consistency. - Removed redundant `.emptyPart` divs and improved spacing with dynamic padding. - Refactored `InfoBubbles` to simplify icon implementation and align styles. - Standardized CMS image handling with enhanced container control and fixed overflow issues. - Added support for children to `Section` components for greater flexibility.
… 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>
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:
