Features — Full-Bleed Rows
blockmarketingAlternating full-bleed feature bands on contrasting surfaces, each pairing a headline and supporting detail with an edge-to-edge visual that runs past the container.
Installation
$pnpm dlx uipkge-ng@latest add feature-full-bleed-rows$npx uipkge-ng@latest add feature-full-bleed-rows$yarn dlx uipkge-ng@latest add feature-full-bleed-rows$bunx uipkge-ng@latest add feature-full-bleed-rowsManifest:
https://uipkge.dev/r/angular/feature-full-bleed-rows.jsonInstalls to:src/app/components/blocks/feature-full-bleed-rows/Variants
Loading interactive previews…
Loading interactive previews…
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
FullBleedRowDetailinterface FullBleedRowDetail {
label: string
value: string
}FullBleedRowinterface FullBleedRow {
eyebrow: string
title: string
body: string
detail: FullBleedRowDetail[]
reverse: boolean
}npm dependencies
Files installed (2)
src/app/components/blocks/feature-full-bleed-rows/feature-full-bleed-rows.component.ts5.2 kB
// Alternating full-bleed feature bands on contrasting surfaces: each row pairs a headline and // supporting detail with an edge-to-edge card that runs past the container edge. Bands alternate // surface as well as side, so the eye has two cues that a new section started. Port of the React // block 1:1. import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { ArrowRight, LucideAngularModule } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiButtonComponent } from '@/ui/button/button.component' import { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component' import { UiSeparatorComponent } from '@/ui/separator/separator.component' export interface FullBleedRowDetail { label: string value: string } export interface FullBleedRow { eyebrow: string title: string body: string detail: FullBleedRowDetail[] reverse: boolean } export const FULL_BLEED_ROWS: FullBleedRow[] = [ { eyebrow: 'Modelling', title: 'The definition is the product', body: 'Dashboards are an output. What your company argues about is the formula behind the number, so that is the thing that gets an owner, a review, and a history.', detail: [ { label: 'Authored in', value: 'Your repository' }, { label: 'Reviewed by', value: 'The named owner' }, { label: 'Reverted in', value: 'One click' }, ], reverse: false, }, { eyebrow: 'Governance', title: 'Permissions that survive a share', body: 'Scope resolves per query from your identity provider, so forwarding a dashboard link never widens what the recipient can see. Every evaluation is logged.', detail: [ { label: 'Resolved at', value: 'Query time' }, { label: 'Sourced from', value: 'SCIM groups' }, { label: 'Retained for', value: '7 years' }, ], reverse: true, }, { eyebrow: 'Operations', title: 'Cost you can see before the invoice', body: 'Query budgets are enforced by the planner before execution, and each dashboard reports what it costs to open. Spend stops being a monthly surprise.', detail: [ { label: 'Enforced at', value: 'Plan time' }, { label: 'Scoped per', value: 'Team' }, { label: 'Reported per', value: 'Dashboard' }, ], reverse: false, }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-feature-full-bleed-rows, [ui-feature-full-bleed-rows]', standalone: true, // React renders the root <section> itself: the host stays out of layout and `class` goes to the root. host: { '[attr.class]': '"contents"' }, imports: [ LucideAngularModule, UiBadgeComponent, UiButtonComponent, UiCardComponent, UiCardContentComponent, UiSeparatorComponent, ], template: ` <section data-slot="feature-full-bleed-rows" [class]="rootClass"> <!-- Bands alternate surface as well as side, so the eye has two cues that a new section started rather than relying on whitespace alone. --> @for (row of rows; track row.title; let index = $index) { <div class="border-border border-b" [class]="index % 2 === 1 ? 'bg-muted/40' : 'bg-background'"> <div class="mx-auto grid max-w-6xl items-center gap-10 px-6 py-16 lg:grid-cols-2 lg:gap-16 lg:py-20"> <div [class]="row.reverse ? 'lg:order-2' : ''"> <span ui-badge variant="secondary">{{ row.eyebrow }}</span> <h2 class="mt-4 text-2xl font-semibold tracking-tight text-balance sm:text-3xl">{{ row.title }}</h2> <p class="text-muted-foreground mt-3 max-w-prose leading-relaxed">{{ row.body }}</p> <button ui-button variant="link" class="mt-4 h-auto p-0"> Read the detail <lucide-icon [img]="ArrowRight" class="ml-1.5 size-3.5" /> </button> </div> <div ui-card [class]=" row.reverse ? 'lg:order-1 lg:-ml-[calc((100vw-100%)/2)] lg:rounded-l-none lg:border-l-0' : 'lg:-mr-[calc((100vw-100%)/2)] lg:rounded-r-none lg:border-r-0' " > <div ui-card-content class="p-0"> <div class="border-border text-muted-foreground border-b px-4 py-2.5 font-mono text-xs"> {{ row.eyebrow.toLowerCase() }} </div> <dl> @for (entry of row.detail; track entry.label; let entryIndex = $index) { <div class="flex items-center justify-between gap-4 px-4 py-3"> <dt class="text-muted-foreground text-sm">{{ entry.label }}</dt> <dd class="text-sm font-medium">{{ entry.value }}</dd> </div> @if (entryIndex < row.detail.length - 1) { <ui-separator /> } } </dl> </div> </div> </div> </div> } </section> `, }) export class UiFeatureFullBleedRowsComponent { protected readonly ArrowRight = ArrowRight protected readonly rows = FULL_BLEED_ROWS @Input('class') className?: string get rootClass(): string { return cn('bg-background', this.className) } }src/app/components/blocks/feature-full-bleed-rows/index.ts0.2 kB
export { UiFeatureFullBleedRowsComponent, FULL_BLEED_ROWS, type FullBleedRow, type FullBleedRowDetail, } from './feature-full-bleed-rows.component'
Raw manifest:https://uipkge.dev/r/angular/feature-full-bleed-rows.json