Foundations

Spacing & Layout

The Bootstrap-derived spacing scale (4px steps), the per-component border-radius scale (6 / 8 / 10px), the box-shadow depths, the fixed sidebar/header dimensions, and the responsive breakpoints every micro-frontend lays out against.

Source in the MFEs
shared-styles _variables.scss (spacers)shared-styles _variables.scss (breakpoints)shared-styles _variables.scss (radius / shadow)administrator-fe layout.scss (sidebar / gutters)header-fe index.scss (header height)

Spacing scale & gutters

There are no bespoke $spacing-* tokens. Spacing derives from Bootstraps $spacers map where the unit $spacer = 1rem = 16px, scaling in 4px increments. Pages apply it through utilities (.p-1/.p-2/.p-3 = 4/8/16px) or hardcoded pixel values that match the scale (6, 8, 12, 16px). The main scroll container, .layout-content, uses a fixed responsive gutter rather than utilities.

The scale, to size

Each bar is one $spacers step rendered at its true pixel width. Step 3 (16px) is the base rhythm unit.

Preview
00px0
14px$spacer / 4
28px$spacer / 2
316px$spacer
424px$spacer * 1.5
548px$spacer * 3
664px$spacer * 4
796px$spacer * 6
// @rvn/shared-styles — bootstrap/_variables.scss ($spacer = 1rem = 16px)
$spacer: 1rem !default;
$spacers: (
  0: 0,
  1: math.div($spacer, 4),    // 4px
  2: math.div($spacer, 2),    // 8px
  3: $spacer,                 // 16px
  4: $spacer * 1.5,           // 24px
  5: $spacer * 3,             // 48px
  6: $spacer * 4,             // 64px
  7: $spacer * 6,             // 96px
  8: $spacer * 8,             // 128px
  9: $spacer * 10,            // 160px
  10: $spacer * 12,           // 192px
  11: $spacer * 14,           // 224px
  12: $spacer * 16,           // 256px
) !default;

$grid-gutter-width: 1.5rem !default;   // 24px — Bootstrap .row > .col gutter
NameTypeDefaultDescription
$spacer1rem16pxThe base unit; the whole scale multiplies off it.
step 1$spacer / 44pxTightest gap — .p-1 / .m-1, vertical menu-item margin.
step 2$spacer / 28px.p-2 / .m-2, sidebar-content vertical padding.
step 3$spacer16px.p-3 / .m-3 — the base rhythm; layout-content side gutter (desktop).
step 4$spacer * 1.524pxAlso $grid-gutter-width — Bootstrap .row > .col column gutter.
step 5–12$spacer * 3…1648–256pxLarge section spacing (48, 64, 96, 128, 160, 192, 224, 256px).
.layout-contentpadding12px 16px 0 16pxMain scroll container: 16px sides / 12px top / 0 bottom (desktop).

Responsive content gutter

Preview
.layout-content — 16px sides, 12px top, 0 bottom
page content
// administrator-fe/src/assets/scss/layout.scss — the main scroll container
.layout-content {
  padding: 12px 16px 0 16px;   // desktop: 16px sides, 12px top, 0 bottom
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

// Tablet + mobile: sides tighten to 12px
@media (max-width: 991.98px) {
  .layout-content { padding: 12px 12px 0 12px; }
}
@media (max-width: 767.98px) {
  .layout-content { padding: 12px 12px 0 12px; }
}
Bottom padding must stay 0
.layout-content keeps 0 bottom padding so the sticky AppFormFooter pins flush to the viewport. report-fe diverges with 40px bottom (a SAS requirement), which breaks footer visibility on small screens — dont copy that.

Border radius

Radii are assigned per component, hardcoded rather than pulled from Bootstraps $border-radius (4px) default. The house scale is 6px for menu items and buttons, 8px for cards, popups and the sticky footer, and 10px for status badges.

The radius steps

4px collapsed-logo tile · 6px menu item / button · 8px card / popup / footer · 10px badge.

Preview
4pxlogo
6pxmenu / btn
8pxcard / popup
10pxbadge
// Per-component radii — hardcoded, NOT the Bootstrap $border-radius (4px) default.
.sidebar-menu :deep(.ant-menu-item),
.sidebar-menu :deep(.ant-menu-submenu-title) {
  border-radius: 6px;   // menu items & buttons
}

.raffles-sidebar-popup { border-radius: 8px; }   // popups & cards
.app-form-footer       { border-radius: 8px; }   // sticky footer bar
.logo-collapsed        { border-radius: 4px; }   // collapsed logo tile
.badge-rvn             { border-radius: 10px !important; }   // status badges
NameTypeDefaultDescription
logo-collapsedborder-radius4pxCollapsed sidebar logo tile.
menu item / buttonborder-radius6px.ant-menu-item, .ant-menu-submenu-title and primary/action buttons.
card / popup / footerborder-radius8px.raffles-sidebar-popup, cards, and the .app-form-footer sticky bar.
badge-rvnborder-radius10px !importantStatus badges (see Components → Badge).
$border-radius.25rem4pxBootstrap default — present for framework consistency but overridden per component.

Elevation (shadows)

Bootstrap ships an sm / md / lg depth scale at 0.075–0.175 black opacity. Layout chrome uses two custom depths instead: a directional rail shadow and a softer popup shadow.

Layout shadows

The sidebar casts a directional 2px 0 4px shadow to the right; popups float on a softer 0 6px 16px.

Preview
.azure-sidebar
2px 0 4px / 0.1
popup
0 6px 16px / 0.12

Bootstrap depth scale (sm / md / lg)

The framework scale, available via $box-shadow-* — used for utility-driven elevation.

Preview
sm
md
lg
// @rvn/shared-styles — bootstrap/_variables.scss (Bootstrap depth scale)
$box-shadow-sm:    0 .125rem .25rem rgba($black, .075) !default;   // sm
$box-shadow:       0 .5rem  1rem   rgba($black, .15)  !default;    // md
$box-shadow-lg:    0 1rem   3rem   rgba($black, .175) !default;    // lg
$box-shadow-inset: inset 0 1px 2px rgba($black, .075) !default;

// Applied in layout.scss — custom depths, not the scale above:
.azure-sidebar        { box-shadow: 2px 0 4px rgba(0, 0, 0, 0.1); }   // rail
.raffles-sidebar-popup{ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12); } // popup
NameTypeDefaultDescription
$box-shadow-sm0 .125rem .25remrgba(0,0,0,.075)Subtle lift — resting cards, hover states.
$box-shadow0 .5rem 1remrgba(0,0,0,.15)Medium — dropdowns, raised surfaces.
$box-shadow-lg0 1rem 3remrgba(0,0,0,.175)Large — modals, overlays.
sidebar rail2px 0 4pxrgba(0,0,0,0.1)Directional shadow to the right of .azure-sidebar.
sidebar popup0 6px 16pxrgba(0,0,0,0.12)Flyout popup for the collapsed rail (.raffles-sidebar-popup).
Rail shadow is not module-scoped
.azure-sidebar { box-shadow: 2px 0 4px rgba(0,0,0,0.1) } is a bare global rule. When several MFEs inject their own copy into the shared appshell <head>, the last-loaded one wins — the same collision risk as background and border colours. Scope any override under an .azure-sidebar--<module> modifier.

Breakpoints & dimensions

Layout responds at Bootstraps breakpoints. Below md (768px) the persistent rail is swapped for a <Drawer> overlay so it no longer squeezes content. A handful of fixed dimensions keep the chrome stable: the header is 64px tall, the sidebar header is 42px, and the collapsed rail is 42px wide.

Responsive breakpoints

Preview
0px
xs
576px
sm
768px
md
992px
lg
1200px
xl
1400px
xxl
// @rvn/shared-styles — bootstrap/_variables.scss
$grid-breakpoints: (
  xs: 0,
  sm: 576px,
  md: 768px,
  lg: 992px,
  xl: 1200px,
  xxl: 1400px
) !default;

// Custom rules use 767.98px / 991.98px to match Bootstrap's exclusive @media
// (AntD treats 768px as inclusive; Bootstrap media queries stop at 767.98px).
NameTypeDefaultDescription
smbreakpoint576pxBelow this (xs), sidebar-header padding tightens to 12px.
mdbreakpoint768pxBelow this the rail becomes a Drawer overlay; content gutter is 12px.
lgbreakpoint992pxDesktop rail rendered; content gutter widens to 16px.
xl / xxlbreakpoint1200 / 1400pxWide desktops — no bespoke layout rules.
header line-heightfixed64px.ant-layout-header — the top-bar height (header-fe index.scss).
sidebar-headermin-height42pxCollapsed sidebar header row; padding 6px 16px.
collapsed railwidth42pxAntD collapsed <Sider>; menu items shrink to 26px height / 9px padding-left.
menu itemheight48pxExpanded desktop item; margin 4px 0 (does not collapse — 8px visual gap).

Fixed chrome dimensions

Preview
header 64px
.ant-layout-header — line-height 64px
rail header 42px
.sidebar-header — min-height 42px, padding 6px 16px
collapsed 42px
2626
// administrator-fe/src/assets/scss/layout.scss + header-fe/src/assets/scss/index.scss
.sidebar-header { min-height: 42px; padding: 6px 16px; border-bottom: 1px solid #e1e5e9; }
.sidebar-content { padding: 8px 0; height: calc(100vh - 120px); }

.sidebar-menu :deep(.ant-menu-item),
.sidebar-menu :deep(.ant-menu-submenu-title) { height: 48px; margin: 4px 0; }

// Collapsed rail = 42px wide → items shrink (only override height + padding-left)
.azure-sidebar.ant-layout-sider-collapsed {
  .ant-menu-item, .ant-menu-submenu-title { height: 26px !important; padding-left: 9px !important; }
}

.ant-layout-header { line-height: 64px !important; }   // fixed top-bar height
768px is exclusive in the media queries
Bootstrap defines md: 768px, but the layout rules key off max-width: 767.98px (and 991.98px) so a viewport at exactly 768px counts as desktop — matching Bootstraps exclusive @media behaviour rather than AntDs inclusive breakpoint.
.sidebar-header { min-height: 42px; padding: 6px 16px; }
.sidebar-content { padding: 8px 0; }
.sidebar-menu :deep(.ant-menu-item) { height: 48px; margin: 4px 0; border-radius: 6px; }
.azure-sidebar.ant-layout-sider-collapsed .ant-menu-item {
  height: 26px !important; padding-left: 9px !important;
}