@aiaiai-pt/design-system 0.25.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,180 @@
1
+ <!--
2
+ @component CallToAction
3
+
4
+ A conversion band: a heading, optional body, and one or two action buttons.
5
+ The reusable "ready to act?" strip at the foot of landing / campaign / content
6
+ pages. Presentational — heading, body and buttons (label + href) are DATA, so
7
+ it drops into any vertical.
8
+
9
+ `variant="accent"` fills the band with the brand accent (text flips to
10
+ `--color-text-on-accent`); `"surface"` (default) is a quiet bordered band.
11
+
12
+ @example
13
+ <CallToAction
14
+ heading="Tem uma ideia para o seu município?"
15
+ body="Submeta a sua proposta ao Orçamento Participativo."
16
+ primary={{ label: "Submeter proposta", href: "/propor" }}
17
+ secondary={{ label: "Ver propostas", href: "/mapa" }}
18
+ variant="accent"
19
+ />
20
+ -->
21
+ <script>
22
+ import Button from "./Button.svelte";
23
+
24
+ let {
25
+ /** @type {string} */
26
+ heading = "",
27
+ /** @type {string} */
28
+ body = "",
29
+ /** @type {2 | 3} Heading level for `heading`. */
30
+ headingLevel = 2,
31
+ /** @type {{ label?: string, href?: string } | undefined} Primary CTA. */
32
+ primary = undefined,
33
+ /** @type {{ label?: string, href?: string } | undefined} Secondary CTA. */
34
+ secondary = undefined,
35
+ /** @type {'surface' | 'accent'} Band fill. */
36
+ variant = "surface",
37
+ /** @type {string} */
38
+ class: className = "",
39
+ ...rest
40
+ } = $props();
41
+ </script>
42
+
43
+ <section class="cta cta-{variant} {className}" {...rest}>
44
+ <div class="cta-inner">
45
+ {#if heading}
46
+ <svelte:element this={`h${headingLevel}`} class="cta-heading"
47
+ >{heading}</svelte:element
48
+ >
49
+ {/if}
50
+ {#if body}<p class="cta-body">{body}</p>{/if}
51
+ {#if (primary && primary.label) || (secondary && secondary.label)}
52
+ <div class="cta-actions">
53
+ {#if variant === "accent"}
54
+ <!-- On an accent (brand-coloured) fill the DS button variants don't
55
+ guarantee contrast (the theme's `secondary`/`ghost` text/bg sit
56
+ too close to the accent). Render explicit INVERTED controls whose
57
+ colours are the on-accent/accent token pair — the same contrast
58
+ ratio as a primary button, just flipped. -->
59
+ {#if primary && primary.label}
60
+ <a class="cta-btn-inverted" href={primary.href || undefined}
61
+ >{primary.label}</a
62
+ >
63
+ {/if}
64
+ {#if secondary && secondary.label}
65
+ <a class="cta-link-on-accent" href={secondary.href || undefined}
66
+ >{secondary.label}</a
67
+ >
68
+ {/if}
69
+ {:else}
70
+ {#if primary && primary.label}
71
+ <Button href={primary.href || undefined} variant="primary"
72
+ >{primary.label}</Button
73
+ >
74
+ {/if}
75
+ {#if secondary && secondary.label}
76
+ <Button href={secondary.href || undefined} variant="ghost"
77
+ >{secondary.label}</Button
78
+ >
79
+ {/if}
80
+ {/if}
81
+ </div>
82
+ {/if}
83
+ </div>
84
+ </section>
85
+
86
+ <style>
87
+ .cta {
88
+ border-radius: var(--card-radius);
89
+ overflow-wrap: break-word;
90
+ }
91
+
92
+ .cta-surface {
93
+ background: var(--color-surface-secondary);
94
+ border: 1px solid var(--color-border);
95
+ }
96
+
97
+ .cta-accent {
98
+ background: var(--color-accent);
99
+ color: var(--color-text-on-accent);
100
+ }
101
+
102
+ .cta-inner {
103
+ display: flex;
104
+ flex-direction: column;
105
+ gap: var(--space-md);
106
+ align-items: flex-start;
107
+ max-width: var(--content-width-wide);
108
+ margin-inline: auto;
109
+ padding: var(--space-2xl) var(--content-padding-x);
110
+ }
111
+
112
+ .cta-heading {
113
+ margin: 0;
114
+ font-family: var(--type-heading-font);
115
+ font-size: var(--type-heading-size);
116
+ line-height: var(--type-heading-leading);
117
+ color: inherit;
118
+ }
119
+
120
+ .cta-surface .cta-heading {
121
+ color: var(--color-text);
122
+ }
123
+
124
+ .cta-body {
125
+ margin: 0;
126
+ max-width: var(--content-width-narrow);
127
+ font-family: var(--type-body-font);
128
+ font-size: var(--type-body-size);
129
+ line-height: var(--type-body-leading);
130
+ color: inherit;
131
+ }
132
+
133
+ .cta-surface .cta-body {
134
+ color: var(--color-text-secondary);
135
+ }
136
+
137
+ .cta-actions {
138
+ display: flex;
139
+ flex-wrap: wrap;
140
+ align-items: center;
141
+ gap: var(--space-md);
142
+ margin-top: var(--space-xs);
143
+ }
144
+
145
+ /* Inverted solid button for the accent band: the on-accent surface as the
146
+ fill, the accent as the text — the primary-button token pair, flipped, so
147
+ it carries the same contrast guarantee against the coloured band. */
148
+ .cta-btn-inverted {
149
+ display: inline-flex;
150
+ align-items: center;
151
+ height: var(--button-md-height);
152
+ padding-inline: var(--button-md-padding-x);
153
+ border-radius: var(--button-radius);
154
+ background: var(--color-text-on-accent);
155
+ color: var(--color-accent);
156
+ font-family: var(--button-font);
157
+ font-size: var(--button-md-font-size);
158
+ letter-spacing: var(--button-tracking);
159
+ text-decoration: none;
160
+ white-space: nowrap;
161
+ }
162
+
163
+ .cta-btn-inverted:focus-visible {
164
+ outline: var(--focus-ring-width) solid var(--color-text-on-accent);
165
+ outline-offset: var(--focus-ring-offset);
166
+ }
167
+
168
+ .cta-link-on-accent {
169
+ color: var(--color-text-on-accent);
170
+ font-family: var(--type-body-font);
171
+ font-size: var(--type-body-size);
172
+ text-decoration: underline;
173
+ text-underline-offset: 0.2em;
174
+ }
175
+
176
+ .cta-link-on-accent:focus-visible {
177
+ outline: var(--focus-ring-width) solid var(--color-text-on-accent);
178
+ outline-offset: var(--focus-ring-offset);
179
+ }
180
+ </style>
@@ -0,0 +1,43 @@
1
+ export default CallToAction;
2
+ type CallToAction = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ /**
7
+ * CallToAction
8
+ *
9
+ * A conversion band: a heading, optional body, and one or two action buttons.
10
+ * The reusable "ready to act?" strip at the foot of landing / campaign / content
11
+ * pages. Presentational — heading, body and buttons (label + href) are DATA, so
12
+ * it drops into any vertical.
13
+ *
14
+ * `variant="accent"` fills the band with the brand accent (text flips to
15
+ * `--color-text-on-accent`); `"surface"` (default) is a quiet bordered band.
16
+ *
17
+ * @example
18
+ * <CallToAction
19
+ * heading="Tem uma ideia para o seu município?"
20
+ * body="Submeta a sua proposta ao Orçamento Participativo."
21
+ * primary={{ label: "Submeter proposta", href: "/propor" }}
22
+ * secondary={{ label: "Ver propostas", href: "/mapa" }}
23
+ * variant="accent"
24
+ * />
25
+ */
26
+ declare const CallToAction: import("svelte").Component<{
27
+ heading?: string;
28
+ body?: string;
29
+ headingLevel?: number;
30
+ primary?: any;
31
+ secondary?: any;
32
+ variant?: string;
33
+ class?: string;
34
+ } & Record<string, any>, {}, "">;
35
+ type $$ComponentProps = {
36
+ heading?: string;
37
+ body?: string;
38
+ headingLevel?: number;
39
+ primary?: any;
40
+ secondary?: any;
41
+ variant?: string;
42
+ class?: string;
43
+ } & Record<string, any>;
@@ -0,0 +1,159 @@
1
+ <!--
2
+ @component FaqList
3
+
4
+ A frequently-asked-questions accordion. Built on native `<details>`/`<summary>`
5
+ so it expand/collapses with zero JS and is keyboard + screen-reader accessible
6
+ out of the box. Distinct from `CollapsibleSection` (filter-group chrome, label
7
+ styling): this renders question-weight summaries for content/help pages.
8
+ Presentational — the Q/A pairs are DATA. Answers accept pre-sanitised HTML
9
+ (the consumer owns the XSS boundary) or plain text.
10
+
11
+ @example
12
+ <FaqList
13
+ heading="Perguntas frequentes"
14
+ items={[
15
+ { q: "Quem pode participar?", a: "Qualquer cidadão recenseado no município." },
16
+ { q: "Como submeto?", aHtml: "<p>Use o <a href='/propor'>formulário</a>.</p>" },
17
+ ]}
18
+ />
19
+ -->
20
+ <script>
21
+ let {
22
+ /** @type {string} Optional heading above the list. */
23
+ heading = "",
24
+ /** @type {2 | 3} Heading level for `heading`. */
25
+ headingLevel = 2,
26
+ /**
27
+ * @type {Array<{ q?: string, a?: string, aHtml?: string }>} The questions.
28
+ * `aHtml` (pre-sanitised) takes precedence over the plain `a` text.
29
+ */
30
+ items = [],
31
+ /** @type {string} */
32
+ class: className = "",
33
+ ...rest
34
+ } = $props();
35
+ </script>
36
+
37
+ <section class="faq {className}" {...rest}>
38
+ {#if heading}
39
+ <svelte:element this={`h${headingLevel}`} class="faq-heading"
40
+ >{heading}</svelte:element
41
+ >
42
+ {/if}
43
+ <div class="faq-list">
44
+ {#each items as item (item.q)}
45
+ <details class="faq-item">
46
+ <summary class="faq-question">
47
+ <span class="faq-question-text">{item.q}</span>
48
+ <svg
49
+ class="faq-caret"
50
+ width="14"
51
+ height="14"
52
+ viewBox="0 0 14 14"
53
+ fill="none"
54
+ aria-hidden="true"
55
+ >
56
+ <path
57
+ d="M3 5L7 10L11 5"
58
+ stroke="currentColor"
59
+ stroke-width="1.5"
60
+ stroke-linecap="round"
61
+ stroke-linejoin="round"
62
+ />
63
+ </svg>
64
+ </summary>
65
+ <div class="faq-answer">
66
+ {#if item.aHtml !== undefined}
67
+ <!-- eslint-disable-next-line svelte/no-at-html-tags — caller-sanitised -->
68
+ {@html item.aHtml}
69
+ {:else if item.a}
70
+ <p>{item.a}</p>
71
+ {/if}
72
+ </div>
73
+ </details>
74
+ {/each}
75
+ </div>
76
+ </section>
77
+
78
+ <style>
79
+ .faq {
80
+ display: flex;
81
+ flex-direction: column;
82
+ gap: var(--space-lg);
83
+ overflow-wrap: break-word;
84
+ }
85
+
86
+ .faq-heading {
87
+ margin: 0;
88
+ font-family: var(--type-heading-font);
89
+ font-size: var(--type-heading-size);
90
+ line-height: var(--type-heading-leading);
91
+ color: var(--color-text);
92
+ }
93
+
94
+ .faq-item {
95
+ border-bottom: 1px solid var(--color-border);
96
+ }
97
+
98
+ .faq-question {
99
+ display: flex;
100
+ align-items: center;
101
+ justify-content: space-between;
102
+ gap: var(--space-md);
103
+ cursor: pointer;
104
+ padding: var(--space-md) 0;
105
+ list-style: none;
106
+ font-family: var(--type-heading-sm-font);
107
+ font-size: var(--type-heading-sm-size);
108
+ line-height: var(--type-heading-sm-leading);
109
+ color: var(--color-text);
110
+ }
111
+
112
+ .faq-question::-webkit-details-marker {
113
+ display: none;
114
+ }
115
+ .faq-question::marker {
116
+ content: "";
117
+ }
118
+
119
+ .faq-question:focus-visible {
120
+ outline: var(--focus-ring-width) solid var(--focus-ring-color);
121
+ outline-offset: var(--focus-ring-offset);
122
+ }
123
+
124
+ /* Flex item: shrink past the caret so a long question wraps/breaks. */
125
+ .faq-question-text {
126
+ min-width: 0;
127
+ }
128
+
129
+ .faq-caret {
130
+ flex-shrink: 0;
131
+ color: var(--color-text-secondary);
132
+ transition: transform var(--duration-fast) var(--easing-default);
133
+ }
134
+
135
+ .faq-item[open] .faq-caret {
136
+ transform: rotate(180deg);
137
+ }
138
+
139
+ .faq-answer {
140
+ padding: 0 0 var(--space-md);
141
+ font-family: var(--type-body-font);
142
+ font-size: var(--type-body-size);
143
+ line-height: var(--type-body-leading);
144
+ color: var(--color-text-secondary);
145
+ }
146
+
147
+ .faq-answer :global(p) {
148
+ margin: 0 0 var(--space-sm);
149
+ }
150
+ .faq-answer :global(p:last-child) {
151
+ margin-bottom: 0;
152
+ }
153
+
154
+ @media (prefers-reduced-motion: reduce) {
155
+ .faq-caret {
156
+ transition: none;
157
+ }
158
+ }
159
+ </style>
@@ -0,0 +1,36 @@
1
+ export default FaqList;
2
+ type FaqList = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ /**
7
+ * FaqList
8
+ *
9
+ * A frequently-asked-questions accordion. Built on native `<details>`/`<summary>`
10
+ * so it expand/collapses with zero JS and is keyboard + screen-reader accessible
11
+ * out of the box. Distinct from `CollapsibleSection` (filter-group chrome, label
12
+ * styling): this renders question-weight summaries for content/help pages.
13
+ * Presentational — the Q/A pairs are DATA. Answers accept pre-sanitised HTML
14
+ * (the consumer owns the XSS boundary) or plain text.
15
+ *
16
+ * @example
17
+ * <FaqList
18
+ * heading="Perguntas frequentes"
19
+ * items={[
20
+ * { q: "Quem pode participar?", a: "Qualquer cidadão recenseado no município." },
21
+ * { q: "Como submeto?", aHtml: "<p>Use o <a href='/propor'>formulário</a>.</p>" },
22
+ * ]}
23
+ * />
24
+ */
25
+ declare const FaqList: import("svelte").Component<{
26
+ heading?: string;
27
+ headingLevel?: number;
28
+ items?: any[];
29
+ class?: string;
30
+ } & Record<string, any>, {}, "">;
31
+ type $$ComponentProps = {
32
+ heading?: string;
33
+ headingLevel?: number;
34
+ items?: any[];
35
+ class?: string;
36
+ } & Record<string, any>;
@@ -0,0 +1,139 @@
1
+ <!--
2
+ @component FeatureGrid
3
+
4
+ A responsive grid of feature cards — each an optional icon, a heading, and a
5
+ short description (optionally a link). The landing/marketing "what you can do"
6
+ / highlights band. Purely presentational: the items are DATA (page props), so
7
+ it drops into any vertical with different copy. Built on CardGrid + Card.
8
+
9
+ Headings render at `headingLevel` (default h3 — the band sits under a section
10
+ heading the consumer owns). Icons are DECORATIVE: the DS stays icon-set
11
+ agnostic, so the consumer supplies an `icon` snippet that renders an icon for
12
+ each item (e.g. mapping `item.icon` to its own icon library). No snippet → no
13
+ icons, cards still render.
14
+
15
+ @example
16
+ <FeatureGrid
17
+ heading="Como participar"
18
+ items={[
19
+ { icon: "lightbulb", title: "Proponha", text: "Submeta a sua ideia." },
20
+ { icon: "check-circle", title: "Vote", text: "Escolha as vencedoras.", href: "/propor" },
21
+ ]}
22
+ >
23
+ {#snippet icon(item)}<MyIcon name={item.icon} />{/snippet}
24
+ </FeatureGrid>
25
+ -->
26
+ <script>
27
+ import CardGrid from "./CardGrid.svelte";
28
+ import Card from "./Card.svelte";
29
+
30
+ let {
31
+ /** @type {string} Optional band heading (rendered above the grid). */
32
+ heading = "",
33
+ /** @type {string} Optional supporting text under the heading. */
34
+ intro = "",
35
+ /** @type {2 | 3 | 4} Heading level for `heading`. */
36
+ headingLevel = 2,
37
+ /** @type {'2' | '3' | '4'} Grid columns at desktop. */
38
+ columns = "3",
39
+ /**
40
+ * @type {Array<{ icon?: string, title?: string, text?: string, href?: string }>}
41
+ * Feature items. `icon` is an opaque name passed to the `icon` snippet;
42
+ * `href` makes the whole card a link.
43
+ */
44
+ items = [],
45
+ /**
46
+ * @type {import('svelte').Snippet<[{ icon?: string }]> | undefined}
47
+ * Decorative per-item icon. Receives the item; the consumer maps `item.icon`
48
+ * to its own icon component (the DS doesn't bundle an icon set).
49
+ */
50
+ icon = undefined,
51
+ /** @type {string} */
52
+ class: className = "",
53
+ ...rest
54
+ } = $props();
55
+ </script>
56
+
57
+ <div class="feature-grid {className}" {...rest}>
58
+ {#if heading}
59
+ <div class="feature-grid-head">
60
+ <svelte:element this={`h${headingLevel}`} class="feature-grid-heading"
61
+ >{heading}</svelte:element
62
+ >
63
+ {#if intro}<p class="feature-grid-intro">{intro}</p>{/if}
64
+ </div>
65
+ {/if}
66
+
67
+ <CardGrid {columns}>
68
+ {#each items as item (item.title)}
69
+ <Card href={item.href || undefined} interactive={!!item.href}>
70
+ {#if icon && item.icon}
71
+ <span class="feature-icon" aria-hidden="true">{@render icon(item)}</span>
72
+ {/if}
73
+ {#if item.title}
74
+ <svelte:element
75
+ this={`h${Math.min(headingLevel + 1, 4)}`}
76
+ class="feature-title">{item.title}</svelte:element
77
+ >
78
+ {/if}
79
+ {#if item.text}<p class="feature-text">{item.text}</p>{/if}
80
+ </Card>
81
+ {/each}
82
+ </CardGrid>
83
+ </div>
84
+
85
+ <style>
86
+ .feature-grid {
87
+ display: flex;
88
+ flex-direction: column;
89
+ gap: var(--space-xl);
90
+ /* Break a pathological unbreakable token instead of blowing out a card
91
+ (CardGrid already sets min-width:0 on its children). Inherited. */
92
+ overflow-wrap: break-word;
93
+ }
94
+
95
+ .feature-grid-head {
96
+ display: flex;
97
+ flex-direction: column;
98
+ gap: var(--space-sm);
99
+ }
100
+
101
+ .feature-grid-heading {
102
+ margin: 0;
103
+ font-family: var(--type-heading-font);
104
+ font-size: var(--type-heading-size);
105
+ line-height: var(--type-heading-leading);
106
+ color: var(--color-text);
107
+ }
108
+
109
+ .feature-grid-intro {
110
+ margin: 0;
111
+ max-width: var(--content-width-narrow);
112
+ font-family: var(--type-body-font);
113
+ font-size: var(--type-body-size);
114
+ color: var(--color-text-secondary);
115
+ }
116
+
117
+ .feature-icon {
118
+ display: inline-flex;
119
+ color: var(--color-accent);
120
+ margin-bottom: var(--space-sm);
121
+ line-height: 0;
122
+ }
123
+
124
+ .feature-title {
125
+ margin: 0 0 var(--space-xs);
126
+ font-family: var(--type-heading-sm-font);
127
+ font-size: var(--type-heading-sm-size);
128
+ line-height: var(--type-heading-sm-leading);
129
+ color: var(--color-text);
130
+ }
131
+
132
+ .feature-text {
133
+ margin: 0;
134
+ font-family: var(--type-body-font);
135
+ font-size: var(--type-body-size);
136
+ line-height: var(--type-body-leading);
137
+ color: var(--color-text-secondary);
138
+ }
139
+ </style>
@@ -0,0 +1,48 @@
1
+ export default FeatureGrid;
2
+ type FeatureGrid = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ /**
7
+ * FeatureGrid
8
+ *
9
+ * A responsive grid of feature cards — each an optional icon, a heading, and a
10
+ * short description (optionally a link). The landing/marketing "what you can do"
11
+ * / highlights band. Purely presentational: the items are DATA (page props), so
12
+ * it drops into any vertical with different copy. Built on CardGrid + Card.
13
+ *
14
+ * Headings render at `headingLevel` (default h3 — the band sits under a section
15
+ * heading the consumer owns). Icons are DECORATIVE: the DS stays icon-set
16
+ * agnostic, so the consumer supplies an `icon` snippet that renders an icon for
17
+ * each item (e.g. mapping `item.icon` to its own icon library). No snippet → no
18
+ * icons, cards still render.
19
+ *
20
+ * @example
21
+ * <FeatureGrid
22
+ * heading="Como participar"
23
+ * items={[
24
+ * { icon: "lightbulb", title: "Proponha", text: "Submeta a sua ideia." },
25
+ * { icon: "check-circle", title: "Vote", text: "Escolha as vencedoras.", href: "/propor" },
26
+ * ]}
27
+ * >
28
+ * {#snippet icon(item)}<MyIcon name={item.icon} />{/snippet}
29
+ * </FeatureGrid>
30
+ */
31
+ declare const FeatureGrid: import("svelte").Component<{
32
+ heading?: string;
33
+ intro?: string;
34
+ headingLevel?: number;
35
+ columns?: string;
36
+ items?: any[];
37
+ icon?: any;
38
+ class?: string;
39
+ } & Record<string, any>, {}, "">;
40
+ type $$ComponentProps = {
41
+ heading?: string;
42
+ intro?: string;
43
+ headingLevel?: number;
44
+ columns?: string;
45
+ items?: any[];
46
+ icon?: any;
47
+ class?: string;
48
+ } & Record<string, any>;
@@ -16,6 +16,11 @@
16
16
 
17
17
  @example Background image (a theme-tinted scrim keeps text contrast)
18
18
  <Hero title="Report a problem" image="/images/city.jpg" />
19
+
20
+ @example Centered marketing hero with a call-to-action
21
+ <Hero title="Participe" subtitle="As suas ideias." align="center" image="/op.jpg">
22
+ {#snippet actions()}<Button href="/propor">Submeter</Button>{/snippet}
23
+ </Hero>
19
24
  -->
20
25
  <script>
21
26
  let {
@@ -28,6 +33,10 @@
28
33
  /** @type {string | undefined} Background image URL — rendered cover/center
29
34
  * under a `--hero-scrim` overlay so the text tokens keep contrast. */
30
35
  image = undefined,
36
+ /** @type {'start' | 'center'} Content alignment — `center` is the marketing
37
+ * variant (centered title/subtitle/actions); `start` (default) is the
38
+ * left-aligned process hero. */
39
+ align = "start",
31
40
  /** @type {string} */
32
41
  class: className = "",
33
42
  /** @type {import('svelte').Snippet | undefined} Title override (rich content). */
@@ -46,7 +55,7 @@
46
55
  </script>
47
56
 
48
57
  <section
49
- class="hero {className}"
58
+ class="hero hero-align-{align} {className}"
50
59
  class:hero-has-image={!!image}
51
60
  style={bgStyle}
52
61
  {...rest}
@@ -69,6 +78,9 @@
69
78
  <style>
70
79
  .hero {
71
80
  background: var(--color-surface);
81
+ /* Break a pathological unbreakable token (long URL/word) instead of
82
+ overflowing the band. overflow-wrap is inherited → covers all text. */
83
+ overflow-wrap: break-word;
72
84
  }
73
85
 
74
86
  .hero-has-image {
@@ -120,4 +132,14 @@
120
132
  gap: var(--space-sm);
121
133
  margin-top: var(--space-sm);
122
134
  }
135
+
136
+ /* Centered marketing variant — title/subtitle/actions all centre-aligned. */
137
+ .hero-align-center .hero-inner {
138
+ align-items: center;
139
+ text-align: center;
140
+ }
141
+
142
+ .hero-align-center .hero-subtitle {
143
+ margin-inline: auto;
144
+ }
123
145
  </style>
@@ -21,12 +21,18 @@ type Hero = {
21
21
  *
22
22
  * @example Background image (a theme-tinted scrim keeps text contrast)
23
23
  * <Hero title="Report a problem" image="/images/city.jpg" />
24
+ *
25
+ * @example Centered marketing hero with a call-to-action
26
+ * <Hero title="Participe" subtitle="As suas ideias." align="center" image="/op.jpg">
27
+ * {#snippet actions()}<Button href="/propor">Submeter</Button>{/snippet}
28
+ * </Hero>
24
29
  */
25
30
  declare const Hero: import("svelte").Component<{
26
31
  title?: string;
27
32
  subtitle?: string;
28
33
  headingLevel?: number;
29
34
  image?: any;
35
+ align?: string;
30
36
  class?: string;
31
37
  children?: any;
32
38
  actions?: any;
@@ -36,6 +42,7 @@ type $$ComponentProps = {
36
42
  subtitle?: string;
37
43
  headingLevel?: number;
38
44
  image?: any;
45
+ align?: string;
39
46
  class?: string;
40
47
  children?: any;
41
48
  actions?: any;