fix(cibg): address fieldset padding + atomic-design audit cleanup

Fix: address-fields shipped a scoped `fieldset { padding:0; margin:0 }` that
tied on specificity with CIBG's `.form-horizontal fieldset` and, injected later,
won — flattening the grey-box padding on "Mijn gegevens" and the registratie
wizard address step. Removed the override (kept the legend rule).

Audit cleanup (minimise atoms, converge or document near-duplicates):
- delete dead `app-card` (+ story; zero consumers, superseded by app-data-block)
  and the unused `.app-info-box` utility
- review-section raw <h2> -> app-heading; registratie-wizard lone <dl> -> app-data-block
- keep application-link's raw <h3> (vendored `.applications li a h3` chain needs
  the bare element) with an inline justification
- document the deliberate "don't merge these" verdicts in a new Convergence
  decisions table in atomic-design.mdx; record the card removal in cibg-gaps.mdx

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
eho
2026-07-20 15:04:02 +02:00
co-authored by Claude Opus 4.8
parent 5e36d68f11
commit 62ba0b98c4
10 changed files with 48 additions and 92 deletions
@@ -19,14 +19,11 @@ export type AdresErrors = Partial<Record<keyof AdresValue, string>>;
@Component({
selector: 'app-address-fields',
imports: [FormsModule, FormFieldComponent, TextInputComponent],
// No scoped `fieldset` reset here: the fieldset must keep CIBG's
// `.form-horizontal fieldset` grey-box padding/margin. A local `fieldset{padding:0}`
// would tie on specificity and (injected later) win, flattening the padding.
styles: [
`
fieldset {
border: 0;
margin: 0;
padding: 0;
min-inline-size: 0;
}
legend {
padding: 0;
font-weight: var(--rhc-text-font-weight-semi-bold);
@@ -7,6 +7,7 @@ import { ButtonComponent } from '@shared/ui/button/button.component';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component';
import { DataRowComponent } from '@shared/ui/data-row/data-row.component';
import { DataBlockComponent } from '@shared/ui/data-block/data-block.component';
import { ReviewSectionComponent } from '@shared/ui/review-section/review-section.component';
import { ConfirmationComponent } from '@shared/ui/confirmation/confirmation.component';
import {
@@ -67,6 +68,7 @@ const NL_TAALVAARDIGHEID_VRAAG = 'nl-taalvaardigheid';
AlertComponent,
SkeletonComponent,
DataRowComponent,
DataBlockComponent,
ReviewSectionComponent,
ConfirmationComponent,
WizardShellComponent,
@@ -216,14 +218,14 @@ const NL_TAALVAARDIGHEID_VRAAG = 'nl-taalvaardigheid';
</app-form-field>
</fieldset>
} @else if (draft().beroep) {
<dl class="mb-0 app-section">
<app-data-block class="app-section">
<div
app-data-row
i18n-key="@@regWizard.beroepAfgeleid"
key="Beroep (afgeleid uit diploma)"
[value]="draft().beroep ?? ''"
></div>
</dl>
</app-data-block>
}
@if (actieveVragen(data).length) {
@@ -48,6 +48,9 @@ import { RouterLink } from '@angular/router';
<ng-content select="[applicationActions]" />
<ng-template #body>
<div class="content">
<!-- Raw <h3>, not <app-heading>: the vendored ".applications li a h3" chain styles
the bare h3 (link-blue); an app-heading host wrapper would sit between and can
break that selector. Documented in atomic-design.mdx (convergence decisions). -->
<h3 class="h3">{{ heading() }}</h3>
@if (subtitle()) {
<div class="subtitle">{{ subtitle() }}</div>
-45
View File
@@ -1,45 +0,0 @@
import { Component, input } from '@angular/core';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
// CIBG-GAP EXTENSION: n/a — no vendored generic-card class; `.app-card` is a
// hand-rolled surface (NOT Bootstrap's `.card`), see cibg-gaps.mdx. Prefer the
// vendored Datablock (WP-12, `app-data-block`) for application/user data.
/** Molecule: a content card. Standardises the repeated card surface (white,
subtle border, rounded, padded) so pages compose cards instead of hand-rolling
a hand-rolled card surface. Optional heading; the rest is projected.
Local class is .app-card (NOT Bootstrap's .card, whose padding-on-body layout differs). */
@Component({
selector: 'app-card',
imports: [HeadingComponent],
styles: [
`
:host {
display: block;
block-size: 100%;
}
.app-card {
background: var(--rhc-color-wit);
border: var(--rhc-border-width-sm) solid var(--rhc-color-border-subtle);
border-radius: var(--rhc-border-radius-md);
padding: var(--rhc-space-max-xl);
block-size: 100%;
box-sizing: border-box;
}
.app-card > * + * {
margin-block-start: var(--rhc-space-max-md);
}
`,
],
template: `
<section class="app-card">
@if (heading()) {
<app-heading [level]="level()">{{ heading() }}</app-heading>
}
<ng-content />
</section>
`,
})
export class CardComponent {
heading = input<string>();
level = input<1 | 2 | 3 | 4 | 5>(3);
}
-24
View File
@@ -1,24 +0,0 @@
import type { Meta, StoryObj } from '@storybook/angular';
import { CardComponent } from './card.component';
const meta: Meta<CardComponent> = {
title: 'Design System/Molecules/Card',
component: CardComponent,
render: (args) => ({
props: args,
template: `
<app-card [heading]="heading" [level]="level">
<p class="rhc-paragraph">Een kaart groepeert samenhangende inhoud op een schone, omkaderde vlak.</p>
</app-card>`,
}),
args: { heading: 'Persoonsgegevens (BRP)', level: 3 },
parameters: {
cibgGap: true,
docs: { description: { component: 'CIBG-gap extension — see Foundations/CIBG Gap Register.' } },
},
};
export default meta;
type Story = StoryObj<CardComponent>;
export const Default: Story = {};
export const ZonderKop: Story = { args: { heading: undefined } };
@@ -4,10 +4,11 @@ import { HeadingComponent } from '@shared/ui/heading/heading.component';
/** Molecule: CIBG Huisstijl **Datablock** (designsystem.cibg.nl/componenten/datablock)
— THE way to show user/application data. A grey `.data-block` surface holds a white
`.block-wrapper` panel with a `<dl>` of projected `<app-data-row>`s. Use `stacked`
(`.data-block--stacked`) when labels/values are long and should stack. Replaces the
`app-card + dl` idiom for data views (the datablock carries its own surface, so it is
NOT wrapped in an `app-card` — see WP-12). When there is no visible `heading`, pass an
`ariaLabel` so the definition list is announced. */
(`.data-block--stacked`) when labels/values are long and should stack. This is the
single data surface (a generic white `app-card` used to exist but was unused and
removed — see WP-12); the datablock carries its own surface, so it is not nested in
another one. When there is no visible `heading`, pass an `ariaLabel` so the definition
list is announced. */
@Component({
selector: 'app-data-block',
imports: [HeadingComponent],
@@ -26,7 +27,7 @@ import { HeadingComponent } from '@shared/ui/heading/heading.component';
})
export class DataBlockComponent {
heading = input('');
/** Heading level when `heading` is set (default h3, matching `app-card`). */
/** Heading level when `heading` is set (default h3). */
level = input<1 | 2 | 3 | 4 | 5>(3);
/** Stacks label above value (`.data-block--stacked`) for long content. */
stacked = input(false);
@@ -1,5 +1,6 @@
import { Component, input, output } from '@angular/core';
import { DataBlockComponent } from '@shared/ui/data-block/data-block.component';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
/** Molecule: one section of a CIBG Huisstijl "controlestap" (wizard review step) —
a heading with a "Wijzigen" link, and the section's `<app-data-row>`s in a CIBG
@@ -7,10 +8,10 @@ import { DataBlockComponent } from '@shared/ui/data-block/data-block.component';
heading and decides what "Wijzigen" does (typically jump back to a step). */
@Component({
selector: 'app-review-section',
imports: [DataBlockComponent],
imports: [DataBlockComponent, HeadingComponent],
template: `
<div class="d-flex">
<h2>{{ heading() }}</h2>
<app-heading [level]="2">{{ heading() }}</app-heading>
@if (showEdit()) {
<div class="ms-auto">
<a href="#" [attr.aria-label]="editAriaLabel() || editLabel()" (click)="onEdit($event)">{{