@c-code/c-code-fw 1.3.0 → 2.1.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.
- package/README.md +118 -104
- package/fesm2022/c-code-c-code-fw-ui.mjs +622 -141
- package/fesm2022/c-code-c-code-fw-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/ui/lib/components/button/button.component.d.ts +24 -0
- package/ui/lib/components/category-menu/category-menu.component.d.ts +12 -3
- package/ui/lib/components/faq/faq.component.d.ts +11 -4
- package/ui/lib/components/faq-item/faq-item.component.d.ts +21 -0
- package/ui/lib/components/gallery/gallery.component.d.ts +7 -3
- package/ui/lib/components/info-item/info-item.component.d.ts +10 -2
- package/ui/lib/components/lightbox/lightbox.component.d.ts +18 -3
- package/ui/lib/components/notice/notice.component.d.ts +17 -0
- package/ui/lib/components/page-banner/page-banner.component.d.ts +9 -3
- package/ui/lib/components/plan-card/plan-card.component.d.ts +29 -6
- package/ui/lib/components/plan-catalog/plan-catalog.component.d.ts +43 -19
- package/ui/lib/components/plan-details/plan-details.component.d.ts +26 -11
- package/ui/lib/components/plan-filter-form/plan-filter-form.component.d.ts +15 -5
- package/ui/lib/components/promo-modal/promo-modal.component.d.ts +39 -7
- package/ui/lib/components/search-box/search-box.component.d.ts +6 -1
- package/ui/lib/components/section-heading/section-heading.component.d.ts +34 -0
- package/ui/lib/components/social-links/social-links.component.d.ts +10 -6
- package/ui/lib/components/whatsapp-button/whatsapp-button.component.d.ts +12 -2
- package/ui/lib/models/promo.models.d.ts +25 -0
- package/ui/lib/utils/promo.utils.d.ts +18 -0
- package/ui/lib/utils/text.utils.d.ts +10 -0
- package/ui/public-api.d.ts +6 -0
- package/ui/theme/component-base.css +136 -0
- package/ui/theme/tokens.css +111 -45
|
@@ -1,24 +1,56 @@
|
|
|
1
|
+
import { ButtonVariant } from '../button/button.component';
|
|
1
2
|
import * as i0 from "@angular/core";
|
|
2
3
|
/**
|
|
3
|
-
* Modal over the page, for example a promotion
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Modal over the page, for example a promotion. Shows `imageSrc`, or the projected
|
|
5
|
+
* content when there is no image, plus an optional call to action.
|
|
6
|
+
*
|
|
7
|
+
* It decides by itself when to open:
|
|
8
|
+
* - `autoOpen` opens it in the browser after `delayMs` (never during SSR, so it is not
|
|
9
|
+
* part of the prerendered page).
|
|
10
|
+
* - `rememberKey` stores the dismissal in localStorage for `rememberDays` days, so it
|
|
11
|
+
* does not reopen on every visit.
|
|
12
|
+
*
|
|
13
|
+
* Closes with the ✕ button, a backdrop click or Escape. Focus moves into the modal,
|
|
14
|
+
* stays inside while open and returns to the previous element on close.
|
|
6
15
|
*
|
|
7
16
|
* ```html
|
|
8
|
-
* <cc-promo-modal
|
|
17
|
+
* <cc-promo-modal imageSrc="assets/images/pop.jpeg" imageAlt="Promoción" [delayMs]="6000"
|
|
18
|
+
* rememberKey="promo-octubre" ctaLabel="Reservar la promo" [ctaHref]="whatsapp" />
|
|
9
19
|
* ```
|
|
20
|
+
*
|
|
21
|
+
* Tokens: --cc-modal-backdrop, --cc-modal-radius, --cc-modal-close-bg, --cc-modal-close-color.
|
|
10
22
|
*/
|
|
11
23
|
export declare class PromoModalComponent {
|
|
12
|
-
/** Whether the modal is visible. Use `[(open)]` to
|
|
24
|
+
/** Whether the modal is visible. Use `[(open)]` to control it from outside. */
|
|
13
25
|
readonly open: import("@angular/core").ModelSignal<boolean>;
|
|
26
|
+
/** Opens the modal by itself in the browser after `delayMs`. */
|
|
27
|
+
readonly autoOpen: import("@angular/core").InputSignal<boolean>;
|
|
28
|
+
readonly delayMs: import("@angular/core").InputSignal<number>;
|
|
29
|
+
/** localStorage key to remember the dismissal. Change it for each new promotion. */
|
|
30
|
+
readonly rememberKey: import("@angular/core").InputSignal<string>;
|
|
31
|
+
readonly rememberDays: import("@angular/core").InputSignal<number>;
|
|
14
32
|
readonly imageSrc: import("@angular/core").InputSignal<string>;
|
|
15
33
|
readonly imageAlt: import("@angular/core").InputSignal<string>;
|
|
16
|
-
/** Makes the image a link
|
|
34
|
+
/** Makes the image a link. */
|
|
17
35
|
readonly link: import("@angular/core").InputSignal<string>;
|
|
36
|
+
/** Button under the image. Hidden when `ctaHref` is empty. */
|
|
37
|
+
readonly ctaLabel: import("@angular/core").InputSignal<string>;
|
|
38
|
+
readonly ctaHref: import("@angular/core").InputSignal<string>;
|
|
39
|
+
readonly ctaVariant: import("@angular/core").InputSignal<ButtonVariant>;
|
|
18
40
|
readonly closeOnBackdrop: import("@angular/core").InputSignal<boolean>;
|
|
19
41
|
readonly closeLabel: import("@angular/core").InputSignal<string>;
|
|
20
42
|
readonly closed: import("@angular/core").OutputEmitterRef<void>;
|
|
43
|
+
private readonly document;
|
|
44
|
+
private readonly box;
|
|
45
|
+
private readonly closeButton;
|
|
46
|
+
private readonly isOpen;
|
|
47
|
+
constructor();
|
|
21
48
|
close(): void;
|
|
49
|
+
/** Keeps Tab and Shift+Tab inside the modal. */
|
|
50
|
+
protected trapFocus(event: KeyboardEvent): void;
|
|
51
|
+
private storage;
|
|
52
|
+
private wasDismissed;
|
|
53
|
+
private remember;
|
|
22
54
|
static ɵfac: i0.ɵɵFactoryDeclaration<PromoModalComponent, never>;
|
|
23
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<PromoModalComponent, "cc-promo-modal", never, { "open": { "alias": "open"; "required": false; "isSignal": true; }; "imageSrc": { "alias": "imageSrc"; "required": false; "isSignal": true; }; "imageAlt": { "alias": "imageAlt"; "required": false; "isSignal": true; }; "link": { "alias": "link"; "required": false; "isSignal": true; }; "closeOnBackdrop": { "alias": "closeOnBackdrop"; "required": false; "isSignal": true; }; "closeLabel": { "alias": "closeLabel"; "required": false; "isSignal": true; }; }, { "open": "openChange"; "closed": "closed"; }, never, ["*"], true, never>;
|
|
55
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<PromoModalComponent, "cc-promo-modal", never, { "open": { "alias": "open"; "required": false; "isSignal": true; }; "autoOpen": { "alias": "autoOpen"; "required": false; "isSignal": true; }; "delayMs": { "alias": "delayMs"; "required": false; "isSignal": true; }; "rememberKey": { "alias": "rememberKey"; "required": false; "isSignal": true; }; "rememberDays": { "alias": "rememberDays"; "required": false; "isSignal": true; }; "imageSrc": { "alias": "imageSrc"; "required": false; "isSignal": true; }; "imageAlt": { "alias": "imageAlt"; "required": false; "isSignal": true; }; "link": { "alias": "link"; "required": false; "isSignal": true; }; "ctaLabel": { "alias": "ctaLabel"; "required": false; "isSignal": true; }; "ctaHref": { "alias": "ctaHref"; "required": false; "isSignal": true; }; "ctaVariant": { "alias": "ctaVariant"; "required": false; "isSignal": true; }; "closeOnBackdrop": { "alias": "closeOnBackdrop"; "required": false; "isSignal": true; }; "closeLabel": { "alias": "closeLabel"; "required": false; "isSignal": true; }; }, { "open": "openChange"; "closed": "closed"; }, never, ["*"], true, never>;
|
|
24
56
|
}
|
|
@@ -5,14 +5,19 @@ import * as i0 from "@angular/core";
|
|
|
5
5
|
* ```html
|
|
6
6
|
* <cc-search-box placeholder="Buscar plan" [(value)]="query" (search)="onSearch($event)" />
|
|
7
7
|
* ```
|
|
8
|
+
*
|
|
9
|
+
* Tokens: --cc-search-bg, --cc-search-border, --cc-search-btn-bg, --cc-search-btn-color,
|
|
10
|
+
* --cc-search-radius.
|
|
8
11
|
*/
|
|
9
12
|
export declare class SearchBoxComponent {
|
|
10
13
|
readonly value: import("@angular/core").ModelSignal<string>;
|
|
11
14
|
readonly placeholder: import("@angular/core").InputSignal<string>;
|
|
15
|
+
/** Accessible name of the field. Defaults to `placeholder`. */
|
|
16
|
+
readonly label: import("@angular/core").InputSignal<string>;
|
|
12
17
|
readonly buttonLabel: import("@angular/core").InputSignal<string>;
|
|
13
18
|
readonly iconSrc: import("@angular/core").InputSignal<string>;
|
|
14
19
|
readonly search: import("@angular/core").OutputEmitterRef<string>;
|
|
15
20
|
onInput(event: Event): void;
|
|
16
21
|
static ɵfac: i0.ɵɵFactoryDeclaration<SearchBoxComponent, never>;
|
|
17
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<SearchBoxComponent, "cc-search-box", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "buttonLabel": { "alias": "buttonLabel"; "required": false; "isSignal": true; }; "iconSrc": { "alias": "iconSrc"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "search": "search"; }, never, never, true, never>;
|
|
22
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<SearchBoxComponent, "cc-search-box", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "buttonLabel": { "alias": "buttonLabel"; "required": false; "isSignal": true; }; "iconSrc": { "alias": "iconSrc"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "search": "search"; }, never, never, true, never>;
|
|
18
23
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
/**
|
|
3
|
+
* Section title with optional eyebrow, icon, subtitle and decorative rule.
|
|
4
|
+
* Use one per page section so every page shares the same heading rhythm.
|
|
5
|
+
*
|
|
6
|
+
* ```html
|
|
7
|
+
* <cc-section-heading title="Planes" subtitle="Para ti, en pareja o en grupo" iconSrc="assets/images/trebol.png" />
|
|
8
|
+
* <cc-section-heading title="Contacto" tone="inverse" align="center" level="h3" />
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* Tokens: --cc-section-heading-color, --cc-section-heading-size, --cc-section-heading-rule,
|
|
12
|
+
* --cc-section-heading-gap, --cc-section-heading-icon-size.
|
|
13
|
+
*/
|
|
14
|
+
export declare class SectionHeadingComponent {
|
|
15
|
+
readonly title: import("@angular/core").InputSignal<string>;
|
|
16
|
+
readonly subtitle: import("@angular/core").InputSignal<string>;
|
|
17
|
+
/** Small text above the title. */
|
|
18
|
+
readonly eyebrow: import("@angular/core").InputSignal<string>;
|
|
19
|
+
readonly iconSrc: import("@angular/core").InputSignal<string>;
|
|
20
|
+
/** Heading level. The visual size does not change with the level. */
|
|
21
|
+
readonly level: import("@angular/core").InputSignal<"h1" | "h2" | "h3">;
|
|
22
|
+
readonly align: import("@angular/core").InputSignal<"center" | "start">;
|
|
23
|
+
/** `default` for light backgrounds, `inverse` for dark ones, `accent` for brand-colored titles. */
|
|
24
|
+
readonly tone: import("@angular/core").InputSignal<"inverse" | "default" | "accent">;
|
|
25
|
+
/** Shows a short accent line under the title. */
|
|
26
|
+
readonly rule: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
|
|
27
|
+
/** `sm` for sub-sections, `md` (default) for page sections, `lg` for heroes. */
|
|
28
|
+
readonly size: import("@angular/core").InputSignal<"sm" | "md" | "lg">;
|
|
29
|
+
readonly italic: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
|
|
30
|
+
/** Id of the heading element, for anchors. */
|
|
31
|
+
readonly headingId: import("@angular/core").InputSignal<string | null>;
|
|
32
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SectionHeadingComponent, never>;
|
|
33
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<SectionHeadingComponent, "cc-section-heading", never, { "title": { "alias": "title"; "required": true; "isSignal": true; }; "subtitle": { "alias": "subtitle"; "required": false; "isSignal": true; }; "eyebrow": { "alias": "eyebrow"; "required": false; "isSignal": true; }; "iconSrc": { "alias": "iconSrc"; "required": false; "isSignal": true; }; "level": { "alias": "level"; "required": false; "isSignal": true; }; "align": { "alias": "align"; "required": false; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "rule": { "alias": "rule"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "italic": { "alias": "italic"; "required": false; "isSignal": true; }; "headingId": { "alias": "headingId"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
34
|
+
}
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { SocialLink } from '../../models/ui.models';
|
|
2
2
|
import * as i0 from "@angular/core";
|
|
3
3
|
/**
|
|
4
|
-
* Row of social network icons that open in a new tab.
|
|
4
|
+
* Row of social network icons that open in a new tab. Each link has a 44px tap area
|
|
5
|
+
* even when the icon is smaller.
|
|
5
6
|
*
|
|
6
7
|
* ```html
|
|
7
|
-
* <cc-social-links [links]="socials" size="
|
|
8
|
+
* <cc-social-links [links]="socials" size="sm" tone="inverse" />
|
|
8
9
|
* ```
|
|
10
|
+
*
|
|
11
|
+
* Tokens: --cc-social-icon-size, --cc-social-gap.
|
|
9
12
|
*/
|
|
10
13
|
export declare class SocialLinksComponent {
|
|
11
14
|
readonly links: import("@angular/core").InputSignal<SocialLink[]>;
|
|
12
|
-
/** Icon
|
|
13
|
-
readonly size: import("@angular/core").InputSignal<
|
|
14
|
-
|
|
15
|
+
/** Icon size: `sm` 1rem, `md` 1.5rem, `lg` 2.5rem. */
|
|
16
|
+
readonly size: import("@angular/core").InputSignal<"sm" | "md" | "lg">;
|
|
17
|
+
/** `inverse` for dark backgrounds: changes the hover and focus colors. */
|
|
18
|
+
readonly tone: import("@angular/core").InputSignal<"inverse" | "default">;
|
|
15
19
|
static ɵfac: i0.ɵɵFactoryDeclaration<SocialLinksComponent, never>;
|
|
16
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<SocialLinksComponent, "cc-social-links", never, { "links": { "alias": "links"; "required": true; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "
|
|
20
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<SocialLinksComponent, "cc-social-links", never, { "links": { "alias": "links"; "required": true; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
17
21
|
}
|
|
@@ -2,18 +2,28 @@ import * as i0 from "@angular/core";
|
|
|
2
2
|
/**
|
|
3
3
|
* Floating WhatsApp button, fixed to a corner of the screen.
|
|
4
4
|
*
|
|
5
|
+
* `variant`:
|
|
6
|
+
* - `icon` (default): round button with the logo.
|
|
7
|
+
* - `extended`: pill with the logo and `label` as visible text.
|
|
8
|
+
*
|
|
5
9
|
* ```html
|
|
6
10
|
* <cc-whatsapp-button [href]="whatsappLink" />
|
|
7
|
-
* <cc-whatsapp-button [href]="whatsappLink"
|
|
11
|
+
* <cc-whatsapp-button [href]="whatsappLink" variant="extended" label="Reservar" position="left" />
|
|
8
12
|
* ```
|
|
13
|
+
*
|
|
14
|
+
* Tokens: --cc-whatsapp, --cc-whatsapp-hover, --cc-whatsapp-size, --cc-whatsapp-offset-x,
|
|
15
|
+
* --cc-whatsapp-offset-y.
|
|
9
16
|
*/
|
|
10
17
|
export declare class WhatsappButtonComponent {
|
|
11
18
|
/** WhatsApp link. Build it with `whatsappUrl(phone, message)`. */
|
|
12
19
|
readonly href: import("@angular/core").InputSignal<string>;
|
|
13
20
|
/** Custom icon. Without it, the WhatsApp logo is drawn as SVG. */
|
|
14
21
|
readonly iconSrc: import("@angular/core").InputSignal<string>;
|
|
22
|
+
/** Accessible name, and the visible text in the `extended` variant. */
|
|
15
23
|
readonly label: import("@angular/core").InputSignal<string>;
|
|
24
|
+
readonly variant: import("@angular/core").InputSignal<"icon" | "extended">;
|
|
25
|
+
readonly size: import("@angular/core").InputSignal<"sm" | "md">;
|
|
16
26
|
readonly position: import("@angular/core").InputSignal<"right" | "left">;
|
|
17
27
|
static ɵfac: i0.ɵɵFactoryDeclaration<WhatsappButtonComponent, never>;
|
|
18
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<WhatsappButtonComponent, "cc-whatsapp-button", never, { "href": { "alias": "href"; "required": true; "isSignal": true; }; "iconSrc": { "alias": "iconSrc"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "position": { "alias": "position"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
28
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<WhatsappButtonComponent, "cc-whatsapp-button", never, { "href": { "alias": "href"; "required": true; "isSignal": true; }; "iconSrc": { "alias": "iconSrc"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "position": { "alias": "position"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
19
29
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The site's promotional popup, as the CMS stores it (`promo.json`). The site passes it to
|
|
3
|
+
* `cc-promo-modal`; `promoState()` and `promoRememberKey()` decide when it shows.
|
|
4
|
+
*/
|
|
5
|
+
export interface PromoConfig {
|
|
6
|
+
/** Off hides the popup whatever the dates say. */
|
|
7
|
+
active: boolean;
|
|
8
|
+
imageSrc: string;
|
|
9
|
+
/** Describes the image for screen readers and search engines. */
|
|
10
|
+
imageAlt: string;
|
|
11
|
+
/** Call to action. Empty hides the button. */
|
|
12
|
+
ctaLabel: string;
|
|
13
|
+
/** WhatsApp message the button opens. Empty makes the button open the general chat. */
|
|
14
|
+
message: string;
|
|
15
|
+
/** Seconds after the page loads before the popup opens. */
|
|
16
|
+
delaySeconds: number;
|
|
17
|
+
/** Days before it shows again to someone who closed it. 0 shows it on every visit. */
|
|
18
|
+
repeatDays: number;
|
|
19
|
+
/** First day it shows (YYYY-MM-DD, site's local time). Empty: from now on. */
|
|
20
|
+
startDate: string;
|
|
21
|
+
/** Last day it shows, included (YYYY-MM-DD). Empty: no end. */
|
|
22
|
+
endDate: string;
|
|
23
|
+
}
|
|
24
|
+
/** Where a promotion is in its life, for the CMS and for the site. */
|
|
25
|
+
export type PromoState = 'off' | 'scheduled' | 'running' | 'ended' | 'incomplete';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { PromoConfig, PromoState } from '../models/promo.models';
|
|
2
|
+
/** Today's date as YYYY-MM-DD in the local time zone. */
|
|
3
|
+
export declare function localDate(now?: Date): string;
|
|
4
|
+
/**
|
|
5
|
+
* Where the promotion is on a given day:
|
|
6
|
+
* - `off`: switched off.
|
|
7
|
+
* - `incomplete`: on, but without an image.
|
|
8
|
+
* - `scheduled`: before `startDate`.
|
|
9
|
+
* - `ended`: after `endDate`.
|
|
10
|
+
* - `running`: shows.
|
|
11
|
+
*/
|
|
12
|
+
export declare function promoState(promo: PromoConfig | null | undefined, now?: Date): PromoState;
|
|
13
|
+
export declare function isPromoRunning(promo: PromoConfig | null | undefined, now?: Date): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* localStorage key that remembers who closed the popup. It changes when the image or the
|
|
16
|
+
* dates change, so a new promotion shows again to people who closed the previous one.
|
|
17
|
+
*/
|
|
18
|
+
export declare function promoRememberKey(promo: PromoConfig): string;
|
|
@@ -13,3 +13,13 @@ export declare function truncateText(text: string, max?: number): string;
|
|
|
13
13
|
* @param message Pre-filled message, optional.
|
|
14
14
|
*/
|
|
15
15
|
export declare function whatsappUrl(phone: string, message?: string): string;
|
|
16
|
+
export interface PriceFormat {
|
|
17
|
+
/** BCP 47 locale. Defaults to "es-CO", which prints 259900 as "$ 259.900". */
|
|
18
|
+
locale?: string;
|
|
19
|
+
/** ISO 4217 code. Defaults to "COP". */
|
|
20
|
+
currency?: string;
|
|
21
|
+
/** Decimals to show. Defaults to 0. */
|
|
22
|
+
digits?: number;
|
|
23
|
+
}
|
|
24
|
+
/** Formats a price with `Intl.NumberFormat`: formatPrice(259900) -> "$ 259.900". */
|
|
25
|
+
export declare function formatPrice(value: number, { locale, currency, digits }?: PriceFormat): string;
|
package/ui/public-api.d.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
export * from './lib/models/plan.models';
|
|
2
2
|
export * from './lib/models/ui.models';
|
|
3
|
+
export * from './lib/models/promo.models';
|
|
3
4
|
export * from './lib/utils/text.utils';
|
|
4
5
|
export * from './lib/utils/plan.utils';
|
|
6
|
+
export * from './lib/utils/promo.utils';
|
|
5
7
|
export * from './lib/services/plan-catalog.service';
|
|
6
8
|
export * from './lib/services/seo.service';
|
|
9
|
+
export * from './lib/components/button/button.component';
|
|
10
|
+
export * from './lib/components/section-heading/section-heading.component';
|
|
11
|
+
export * from './lib/components/notice/notice.component';
|
|
12
|
+
export * from './lib/components/faq-item/faq-item.component';
|
|
7
13
|
export * from './lib/components/whatsapp-button/whatsapp-button.component';
|
|
8
14
|
export * from './lib/components/promo-modal/promo-modal.component';
|
|
9
15
|
export * from './lib/components/page-banner/page-banner.component';
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Shared base for every @c-code/c-code-fw/ui component. Each component lists this file
|
|
3
|
+
* first in `styleUrls`, so the rules below are scoped to that component.
|
|
4
|
+
*
|
|
5
|
+
* The library does not own any brand. It reads a contract of semantic roles (--cc-*)
|
|
6
|
+
* that each site defines from its own palette and fonts, and resolves them into private
|
|
7
|
+
* --_* variables on the component host. Without a site theme, components render in
|
|
8
|
+
* neutral grays. Resolution happens on each host, so a role set on any ancestor element
|
|
9
|
+
* re-themes the components inside it.
|
|
10
|
+
*
|
|
11
|
+
* Structure (spacing, type scale, radii, shadows, motion) does belong to the library:
|
|
12
|
+
* those tokens have library defaults and components expose props to pick from them.
|
|
13
|
+
*/
|
|
14
|
+
:host {
|
|
15
|
+
/* ---- Color roles: defined by the site ---- */
|
|
16
|
+
--_canvas: var(--cc-canvas, #ffffff); /* page background behind components */
|
|
17
|
+
--_surface: var(--cc-surface, #f5f5f4); /* cards, panels */
|
|
18
|
+
--_surface-alt: var(--cc-surface-alt, #fafaf9); /* alternative band, image placeholders */
|
|
19
|
+
--_text: var(--cc-text, #292524);
|
|
20
|
+
--_text-muted: var(--cc-text-muted, color-mix(in srgb, var(--_text) 72%, transparent));
|
|
21
|
+
--_heading: var(--cc-heading, #1c1917);
|
|
22
|
+
--_accent: var(--cc-accent, #1c1917); /* primary buttons, active items */
|
|
23
|
+
--_accent-hover: var(--cc-accent-hover, #44403c);
|
|
24
|
+
--_accent-text: var(--cc-accent-text, var(--_heading)); /* accent used as text, ≥4.5:1 */
|
|
25
|
+
--_on-accent: var(--cc-on-accent, #ffffff);
|
|
26
|
+
--_inverse: var(--cc-inverse, #1c1917); /* dark blocks: price badge, dark buttons */
|
|
27
|
+
--_inverse-hover: var(--cc-inverse-hover, #000000);
|
|
28
|
+
--_on-inverse: var(--cc-on-inverse, #ffffff);
|
|
29
|
+
--_border: var(--cc-border, #d6d3d1);
|
|
30
|
+
--_border-strong: var(--cc-border-strong, #78716c); /* inputs, ≥3:1 on white */
|
|
31
|
+
--_focus-ring: var(--cc-focus-ring, var(--_heading));
|
|
32
|
+
--_overlay: var(--cc-overlay, rgb(0 0 0 / 0.7));
|
|
33
|
+
--_danger: var(--cc-danger, #b91c1c);
|
|
34
|
+
--_danger-surface: var(--cc-danger-surface, #fef2f2);
|
|
35
|
+
--_on-danger-surface: var(--cc-on-danger-surface, #7f1d1d);
|
|
36
|
+
--_whatsapp: var(--cc-whatsapp, #1faf38);
|
|
37
|
+
--_whatsapp-hover: var(--cc-whatsapp-hover, #17902d);
|
|
38
|
+
--_on-whatsapp: var(--cc-on-whatsapp, #ffffff);
|
|
39
|
+
|
|
40
|
+
/* ---- Typography roles: defined by the site ---- */
|
|
41
|
+
--_font-body: var(--cc-font-body, inherit);
|
|
42
|
+
--_font-heading: var(--cc-font-heading, inherit);
|
|
43
|
+
--_heading-style: var(--cc-heading-style, normal); /* normal | italic */
|
|
44
|
+
--_heading-transform: var(--cc-heading-transform, none); /* none | uppercase */
|
|
45
|
+
--_heading-tracking: var(--cc-heading-tracking, normal);
|
|
46
|
+
|
|
47
|
+
/* ---- Structure: owned by the library ---- */
|
|
48
|
+
--_text-xs: var(--cc-text-xs, 0.75rem);
|
|
49
|
+
--_text-sm: var(--cc-text-sm, 0.875rem);
|
|
50
|
+
--_text-md: var(--cc-text-md, 1rem);
|
|
51
|
+
--_text-lg: var(--cc-text-lg, 1.125rem);
|
|
52
|
+
--_text-xl: var(--cc-text-xl, 1.25rem);
|
|
53
|
+
--_text-2xl: var(--cc-text-2xl, 1.5rem);
|
|
54
|
+
--_text-3xl: var(--cc-text-3xl, clamp(1.625rem, 1.2rem + 1.6vw, 2rem));
|
|
55
|
+
--_text-4xl: var(--cc-text-4xl, clamp(2rem, 1.4rem + 2.4vw, 2.75rem));
|
|
56
|
+
--_leading-tight: var(--cc-leading-tight, 1.2);
|
|
57
|
+
--_leading-snug: var(--cc-leading-snug, 1.4);
|
|
58
|
+
--_leading-body: var(--cc-leading-body, 1.6);
|
|
59
|
+
--_weight-regular: var(--cc-weight-regular, 400);
|
|
60
|
+
--_weight-medium: var(--cc-weight-medium, 500);
|
|
61
|
+
--_weight-semibold: var(--cc-weight-semibold, 600);
|
|
62
|
+
--_weight-bold: var(--cc-weight-bold, 700);
|
|
63
|
+
--_tracking-wide: var(--cc-tracking-wide, 0.04em);
|
|
64
|
+
|
|
65
|
+
--_space-1: var(--cc-space-1, 0.25rem);
|
|
66
|
+
--_space-2: var(--cc-space-2, 0.5rem);
|
|
67
|
+
--_space-3: var(--cc-space-3, 0.75rem);
|
|
68
|
+
--_space-4: var(--cc-space-4, 1rem);
|
|
69
|
+
--_space-5: var(--cc-space-5, 1.25rem);
|
|
70
|
+
--_space-6: var(--cc-space-6, 1.5rem);
|
|
71
|
+
--_space-8: var(--cc-space-8, 2rem);
|
|
72
|
+
--_space-10: var(--cc-space-10, 2.5rem);
|
|
73
|
+
--_space-12: var(--cc-space-12, 3rem);
|
|
74
|
+
--_space-16: var(--cc-space-16, 4rem);
|
|
75
|
+
--_space-20: var(--cc-space-20, 5rem);
|
|
76
|
+
--_space-24: var(--cc-space-24, 6rem);
|
|
77
|
+
|
|
78
|
+
--_radius: var(--cc-radius, 0);
|
|
79
|
+
--_radius-sm: var(--cc-radius-sm, 0.125rem);
|
|
80
|
+
--_radius-md: var(--cc-radius-md, 0.5rem);
|
|
81
|
+
--_radius-lg: var(--cc-radius-lg, 1rem);
|
|
82
|
+
--_radius-pill: var(--cc-radius-pill, 9999px);
|
|
83
|
+
--_shadow-sm: var(--cc-shadow-sm, 0 1px 2px 0 rgb(0 0 0 / 0.05));
|
|
84
|
+
--_shadow: var(--cc-shadow, 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1));
|
|
85
|
+
--_shadow-lg: var(--cc-shadow-lg, 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1));
|
|
86
|
+
--_shadow-xl: var(--cc-shadow-xl, 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1));
|
|
87
|
+
|
|
88
|
+
--_focus-width: var(--cc-focus-width, 2px);
|
|
89
|
+
--_focus-offset: var(--cc-focus-offset, 2px);
|
|
90
|
+
--_control-height: var(--cc-control-height, 2.75rem);
|
|
91
|
+
--_control-height-sm: var(--cc-control-height-sm, 2.25rem);
|
|
92
|
+
--_control-height-lg: var(--cc-control-height-lg, 3.25rem);
|
|
93
|
+
--_duration-fast: var(--cc-duration-fast, 150ms);
|
|
94
|
+
--_duration: var(--cc-duration, 250ms);
|
|
95
|
+
--_ease: var(--cc-ease, cubic-bezier(0.2, 0, 0, 1));
|
|
96
|
+
--_z-float: var(--cc-z-float, 20);
|
|
97
|
+
--_z-sticky: var(--cc-z-sticky, 30);
|
|
98
|
+
--_z-overlay: var(--cc-z-overlay, 50);
|
|
99
|
+
|
|
100
|
+
box-sizing: border-box;
|
|
101
|
+
font-family: var(--_font-body);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
*,
|
|
105
|
+
*::before,
|
|
106
|
+
*::after {
|
|
107
|
+
box-sizing: border-box;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
:focus-visible {
|
|
111
|
+
outline: var(--_focus-width) solid var(--_focus-ring);
|
|
112
|
+
outline-offset: var(--_focus-offset);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* Visually hidden text for screen readers. */
|
|
116
|
+
.cc-sr-only {
|
|
117
|
+
position: absolute;
|
|
118
|
+
width: 1px;
|
|
119
|
+
height: 1px;
|
|
120
|
+
padding: 0;
|
|
121
|
+
margin: -1px;
|
|
122
|
+
overflow: hidden;
|
|
123
|
+
clip: rect(0, 0, 0, 0);
|
|
124
|
+
white-space: nowrap;
|
|
125
|
+
border: 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@media (prefers-reduced-motion: reduce) {
|
|
129
|
+
*,
|
|
130
|
+
*::before,
|
|
131
|
+
*::after {
|
|
132
|
+
animation-duration: 0.01ms !important;
|
|
133
|
+
animation-iteration-count: 1 !important;
|
|
134
|
+
transition-duration: 0.01ms !important;
|
|
135
|
+
}
|
|
136
|
+
}
|
package/ui/theme/tokens.css
CHANGED
|
@@ -1,45 +1,111 @@
|
|
|
1
|
-
/*
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* --cc-
|
|
18
|
-
* --cc-
|
|
19
|
-
* --cc-
|
|
20
|
-
* --cc-accent
|
|
21
|
-
* --cc-accent-
|
|
22
|
-
* --cc-on-accent
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
|
|
27
|
-
:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
1
|
+
/*
|
|
2
|
+
* Structural tokens of @c-code/c-code-fw/ui: type scale, spacing, radii, shadows,
|
|
3
|
+
* controls, motion and layers.
|
|
4
|
+
*
|
|
5
|
+
* Every component already falls back to these values, so this file is optional. Load it
|
|
6
|
+
* (angular.json → styles) when the site wants to use the same scales in its own markup,
|
|
7
|
+
* for example by mapping Tailwind's font sizes and spacing to var(--cc-text-*) and
|
|
8
|
+
* var(--cc-space-*). Override any of them to change the rhythm of a whole site.
|
|
9
|
+
*
|
|
10
|
+
* COLORS AND FONTS ARE NOT HERE. They belong to each site. The components read this
|
|
11
|
+
* contract of roles, which the site sets from its own palette (with any variable names):
|
|
12
|
+
*
|
|
13
|
+
* --cc-canvas page background behind components
|
|
14
|
+
* --cc-surface cards, panels, sidebar
|
|
15
|
+
* --cc-surface-alt alternative band, image placeholders
|
|
16
|
+
* --cc-text body text
|
|
17
|
+
* --cc-text-muted counters, metadata (default: --cc-text at 72%)
|
|
18
|
+
* --cc-heading titles
|
|
19
|
+
* --cc-accent primary buttons, active items
|
|
20
|
+
* --cc-accent-hover primary button hover
|
|
21
|
+
* --cc-accent-text accent used as text; needs 4.5:1 on --cc-canvas and --cc-surface
|
|
22
|
+
* --cc-on-accent text on --cc-accent; needs 4.5:1
|
|
23
|
+
* --cc-inverse dark blocks: price badge, dark buttons
|
|
24
|
+
* --cc-inverse-hover
|
|
25
|
+
* --cc-on-inverse text on --cc-inverse
|
|
26
|
+
* --cc-border dividers
|
|
27
|
+
* --cc-border-strong input borders, 3:1 on --cc-canvas
|
|
28
|
+
* --cc-focus-ring keyboard focus outline
|
|
29
|
+
* --cc-overlay modal and lightbox backdrop
|
|
30
|
+
* --cc-danger, --cc-danger-surface, --cc-on-danger-surface warnings
|
|
31
|
+
* --cc-whatsapp, --cc-whatsapp-hover WhatsApp buttons
|
|
32
|
+
* --cc-font-body, --cc-font-heading font families
|
|
33
|
+
* --cc-heading-style (normal|italic), --cc-heading-transform, --cc-heading-tracking
|
|
34
|
+
*
|
|
35
|
+
* Example, in the site's styles.css:
|
|
36
|
+
*
|
|
37
|
+
* :root {
|
|
38
|
+
* --laurel-green: #4c6b4a;
|
|
39
|
+
* --laurel-gold: #ceab5d;
|
|
40
|
+
* --cc-heading: var(--laurel-green);
|
|
41
|
+
* --cc-accent: var(--laurel-gold);
|
|
42
|
+
* --cc-on-accent: #022b04;
|
|
43
|
+
* }
|
|
44
|
+
*
|
|
45
|
+
* Without a site theme the components render in neutral grays.
|
|
46
|
+
*
|
|
47
|
+
* Breakpoints (media queries cannot use variables): sm 640px, md 768px, lg 1024px, xl 1280px.
|
|
48
|
+
*/
|
|
49
|
+
:root {
|
|
50
|
+
/* Type scale */
|
|
51
|
+
--cc-text-xs: 0.75rem;
|
|
52
|
+
--cc-text-sm: 0.875rem;
|
|
53
|
+
--cc-text-md: 1rem;
|
|
54
|
+
--cc-text-lg: 1.125rem;
|
|
55
|
+
--cc-text-xl: 1.25rem;
|
|
56
|
+
--cc-text-2xl: 1.5rem;
|
|
57
|
+
--cc-text-3xl: clamp(1.625rem, 1.2rem + 1.6vw, 2rem);
|
|
58
|
+
--cc-text-4xl: clamp(2rem, 1.4rem + 2.4vw, 2.75rem);
|
|
59
|
+
--cc-leading-tight: 1.2;
|
|
60
|
+
--cc-leading-snug: 1.4;
|
|
61
|
+
--cc-leading-body: 1.6;
|
|
62
|
+
--cc-weight-regular: 400;
|
|
63
|
+
--cc-weight-medium: 500;
|
|
64
|
+
--cc-weight-semibold: 600;
|
|
65
|
+
--cc-weight-bold: 700;
|
|
66
|
+
--cc-tracking-wide: 0.04em;
|
|
67
|
+
|
|
68
|
+
/* Spacing (4px base, same steps as Tailwind) */
|
|
69
|
+
--cc-space-1: 0.25rem;
|
|
70
|
+
--cc-space-2: 0.5rem;
|
|
71
|
+
--cc-space-3: 0.75rem;
|
|
72
|
+
--cc-space-4: 1rem;
|
|
73
|
+
--cc-space-5: 1.25rem;
|
|
74
|
+
--cc-space-6: 1.5rem;
|
|
75
|
+
--cc-space-8: 2rem;
|
|
76
|
+
--cc-space-10: 2.5rem;
|
|
77
|
+
--cc-space-12: 3rem;
|
|
78
|
+
--cc-space-16: 4rem;
|
|
79
|
+
--cc-space-20: 5rem;
|
|
80
|
+
--cc-space-24: 6rem;
|
|
81
|
+
|
|
82
|
+
/* Shape */
|
|
83
|
+
--cc-radius: 0; /* surfaces: cards, panels */
|
|
84
|
+
--cc-radius-sm: 0.125rem;
|
|
85
|
+
--cc-radius-md: 0.5rem;
|
|
86
|
+
--cc-radius-lg: 1rem;
|
|
87
|
+
--cc-radius-pill: 9999px;
|
|
88
|
+
--cc-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
89
|
+
--cc-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
|
90
|
+
--cc-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
|
91
|
+
--cc-shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
|
92
|
+
|
|
93
|
+
/* Focus */
|
|
94
|
+
--cc-focus-width: 2px;
|
|
95
|
+
--cc-focus-offset: 2px;
|
|
96
|
+
|
|
97
|
+
/* Controls */
|
|
98
|
+
--cc-control-height: 2.75rem; /* 44px minimum tap target */
|
|
99
|
+
--cc-control-height-sm: 2.25rem;
|
|
100
|
+
--cc-control-height-lg: 3.25rem;
|
|
101
|
+
|
|
102
|
+
/* Motion */
|
|
103
|
+
--cc-duration-fast: 150ms;
|
|
104
|
+
--cc-duration: 250ms;
|
|
105
|
+
--cc-ease: cubic-bezier(0.2, 0, 0, 1);
|
|
106
|
+
|
|
107
|
+
/* Layers */
|
|
108
|
+
--cc-z-float: 20;
|
|
109
|
+
--cc-z-sticky: 30;
|
|
110
|
+
--cc-z-overlay: 50;
|
|
111
|
+
}
|