Member Portal — design system & conventions¶
Shared visual conventions for the member portal (the {permalink}.raklet.com
app — Application/ Razor views + Raklet.Backend/Content/styles/core/social/*.scss).
Follow these when building or restyling portal pages so headers, cards, buttons,
inputs, and brand colour stay consistent. This codifies the conventions established
during the portal modernization (PR #14021); see also
Member portal modernization — design.
Scope: the member-facing portal only. The AngularJS admin SPA (
admin.raklet.com) has its own styling and is out of scope here.
Build & deploy¶
- SCSS lives in
Raklet.Backend/Content/styles/core/social/(compiles toContent/dist/styles/social.css→ minifiedsocial.min.css, served frombackend.raklet.com/content/dist/styles/). After editing SCSS:cd Raklet.Backend && npx grunt dart-sass cssmin.Content/dist/is gitignored — commit only the.scsssource; CI/deploy rebuilds it. - Razor views (
Application/.../*.cshtml) deploy directly. Escape any literal@in inline<style>/<script>as@@or the page 500s. - Controller/.cs changes need an MSBuild (
scripts/dev/build-fast.ps1 -Site portal).
Brand colour¶
- The brand accent is per-org, themed by a generated
branding.css(Azure blob customcode), exposed to CSS as--portal-accent(and--portal-brandfor the header bar). Use these variables — never hardcode a brand colour. .btn-primary, links, focus rings, and active states are all driven by the org's branding. Do not override them with a fixed colour.
Cards¶
The canonical card (reference: the /c/introductions post cards):
background: #fff;
border: 1px solid rgba(0, 0, 0, 0.125);
border-radius: 0.5rem;
margin-bottom: 1rem; // spacing between cards
transition: box-shadow 0.15s ease, border-color 0.15s ease;
&:hover { // only when the card is clickable
box-shadow: 0 4px 14px rgba(16, 24, 40, 0.08);
border-color: rgba(0, 0, 0, 0.2);
}
- List pages render each item as its own card, not divider-separated rows in
one big card. When the markup wraps items in a container card, neutralize the
wrapper (
border: none; background: transparent; padding: 0;) so each item is the card. Applied to: Jobs (.JobPostingList), Directory (.directoryItem), Connections — cities/countries/tags/sectors/institutions (#listView .list-group-item), Subscription plans (.subscription-card). - Reserve the hover lift for clickable cards. Static info cards keep the border without the hover.
Inputs & selects¶
border-radius: 8px.- Focus:
border-color: var(--portal-accent)with a soft neutral ring (box-shadow: 0 0 0 3px rgba(0,0,0,0.06)). Never the Bootstrap default blue (#3b82f6). - Native
<select>option lists are OS-rendered and cannot be styled cross-browser — style the closed control only.
Buttons¶
- Primary action:
.btn-primary(inherits the org brand). Use for the main CTA per view (Search, Send Message, Select Plan, Post a Job). - Avoid
.btn-outline-secondaryon coloured backgrounds —branding.cssgives it a hard black border that clashes on the brand-coloured header. The header search was unified into one translucent control instead (.SocialHeader-search:rgba(255,255,255,.16)fill,rgba(255,255,255,.28)border, white icon).
Contrast traps (two recurring non-obvious failures)¶
<a class="btn btn-primary" onclick=…>with nohrefloses its label colour. Bootstrap 4 reboot'sa:not([href]):not([tabindex]){color:inherit}has higher specificity thanbranding.css's.btn-primary{color:#fff}— the button keeps its branded background but the text inherits body colour (dark-on-dark). Fix: markup only — addrole="button" tabindex="0"(orhref="#"). A CSS rule alone cannot fix this because the button colour is per-org (set inbranding.css).color: var(--portal-accent)as a foreground on a white surface (~1.1:1 contrast on light-accent orgs such asdemo.raklet.netwith yellowrgb(255,220,0)). Usecolor-mix(in srgb, var(--portal-accent) 35%, var(--portal-ink,#1a2233))or compute on-accent foregrounds atbranding.cssgeneration time. These bugs reproduce only on light-accent orgs — always verify ondemo.raklet.net, not just on gercek (dark orange accent).
Page headers¶
Headers are the least consistent area today and the main target of the ongoing consistency work:
- Preferred pattern: the shared
.card.PageHeaderwith ab.h2title (Directory and Jobs follow this). - Several pages still use
<h4>or bespoke per-page headers (e.g. Announcements, Members). These should converge on one pattern.
Touch targets¶
- Interactive controls ≥ 44×44px hit area (pad the control; the visible icon can stay smaller). Bottom-bar tabs are 60px; header icon controls (hamburger, back, drawer close) are padded to 44px.
SCSS file map¶
| Area | File |
|---|---|
| Header / nav / search | layout/social-header.scss |
| Shared content/layout | layout/social-content.scss |
| Mobile nav drawer / account sheet | pages/social-mobile-navigation.scss |
| Directory | pages/social-directory.scss |
| Jobs | pages/social-job-board.scss |
| Connections (cities/countries/tags/sectors) | pages/social-connections.scss |
| Events | pages/social-events.scss |
| Profile (self) | pages/social-profile.scss |
| Member-details modal | pages/social-member-details.scss |
| Membership / subscription | pages/social-membership.scss |
| Announcements / boards | pages/social-announcement.scss |
Ongoing work¶
A full design-consistency pass is planned: codify the above into a project
DESIGN.md via gstack /design-consultation, then enforce it page-by-page with
/design-review --deep against the live portal. Page-header unification is the top
item.