@cartbase/storefront 0.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/package.json +226 -0
- package/src/api/auth.ts +108 -0
- package/src/api/carts.ts +506 -0
- package/src/api/categories.ts +184 -0
- package/src/api/checkout.ts +440 -0
- package/src/api/collections.ts +130 -0
- package/src/api/consent.ts +75 -0
- package/src/api/content.ts +125 -0
- package/src/api/customers.ts +307 -0
- package/src/api/gift-cards.ts +112 -0
- package/src/api/http.ts +122 -0
- package/src/api/index.ts +29 -0
- package/src/api/integrations.ts +130 -0
- package/src/api/menus.ts +77 -0
- package/src/api/metaobjects.ts +136 -0
- package/src/api/orders.ts +290 -0
- package/src/api/products.ts +303 -0
- package/src/api/redirects.ts +37 -0
- package/src/api/regions.ts +141 -0
- package/src/api/reviews.ts +259 -0
- package/src/api/search.ts +133 -0
- package/src/api/types.ts +91 -0
- package/src/cart-drawer/cart-drawer.tsx +86 -0
- package/src/cart-drawer/context.tsx +569 -0
- package/src/cart-drawer/continue-shopping.tsx +28 -0
- package/src/cart-drawer/cross-sell-carousel.tsx +212 -0
- package/src/cart-drawer/cross-sell-sidebar.tsx +158 -0
- package/src/cart-drawer/empty.tsx +52 -0
- package/src/cart-drawer/free-gift.tsx +71 -0
- package/src/cart-drawer/gift-wrap.tsx +83 -0
- package/src/cart-drawer/header.tsx +52 -0
- package/src/cart-drawer/index.ts +69 -0
- package/src/cart-drawer/item/index.tsx +164 -0
- package/src/cart-drawer/item/quantity.tsx +100 -0
- package/src/cart-drawer/item/upsell.tsx +110 -0
- package/src/cart-drawer/item/variant.tsx +46 -0
- package/src/cart-drawer/labels-bg.ts +72 -0
- package/src/cart-drawer/labels.ts +119 -0
- package/src/cart-drawer/notes.tsx +131 -0
- package/src/cart-drawer/payment-badges.tsx +96 -0
- package/src/cart-drawer/promo-banner.tsx +43 -0
- package/src/cart-drawer/rewards-points.tsx +78 -0
- package/src/cart-drawer/sticky-footer.tsx +73 -0
- package/src/cart-drawer/summary-breakdown.tsx +196 -0
- package/src/cart-drawer/template.tsx +225 -0
- package/src/cart-drawer/tiered-progress.tsx +168 -0
- package/src/checkout/address-error-copy.ts +119 -0
- package/src/checkout/address-form.tsx +224 -0
- package/src/checkout/address-select.tsx +79 -0
- package/src/checkout/boxnow-locker-selector.tsx +410 -0
- package/src/checkout/checkout-client.tsx +222 -0
- package/src/checkout/company-details.tsx +94 -0
- package/src/checkout/compare-addresses.ts +40 -0
- package/src/checkout/context.tsx +76 -0
- package/src/checkout/discount-section.tsx +218 -0
- package/src/checkout/econt-office-selector.tsx +332 -0
- package/src/checkout/error-message.tsx +25 -0
- package/src/checkout/geocode.ts +154 -0
- package/src/checkout/gift-card-section.tsx +224 -0
- package/src/checkout/index.ts +74 -0
- package/src/checkout/labels-bg.ts +128 -0
- package/src/checkout/labels.ts +263 -0
- package/src/checkout/line-item-card.tsx +152 -0
- package/src/checkout/order-summary.tsx +524 -0
- package/src/checkout/payment-button.tsx +373 -0
- package/src/checkout/payment-error-copy.ts +269 -0
- package/src/checkout/payment-method-list.tsx +365 -0
- package/src/checkout/payment-wrapper.tsx +102 -0
- package/src/checkout/promotion-error-copy.ts +124 -0
- package/src/checkout/shipping-method-list.tsx +335 -0
- package/src/checkout/stripe-wrapper.tsx +165 -0
- package/src/checkout/use-checkout-orchestration.ts +1504 -0
- package/src/common/cart-button-client.tsx +39 -0
- package/src/common/cart-button.tsx +28 -0
- package/src/common/country-select.tsx +65 -0
- package/src/common/delete-button.tsx +66 -0
- package/src/common/index.ts +17 -0
- package/src/common/language-select.tsx +78 -0
- package/src/common/localized-link.tsx +45 -0
- package/src/common/skeleton.tsx +29 -0
- package/src/index.ts +12 -0
- package/src/lib/cart-helpers.ts +113 -0
- package/src/lib/dual-price.tsx +73 -0
- package/src/lib/get-percentage-diff.ts +5 -0
- package/src/lib/get-product-price.ts +133 -0
- package/src/lib/hooks/use-intersection.ts +30 -0
- package/src/lib/hooks/use-toggle-state.ts +25 -0
- package/src/lib/money.ts +73 -0
- package/src/lib/payment-constants.ts +66 -0
- package/src/lib/product.ts +22 -0
- package/src/lib/sort-products.ts +63 -0
- package/src/lib/store-api-error.ts +36 -0
- package/src/lib/utils.ts +16 -0
- package/src/order/context.tsx +32 -0
- package/src/order/index.ts +63 -0
- package/src/order/labels-bg.ts +39 -0
- package/src/order/labels.ts +79 -0
- package/src/order/order-address-card.tsx +47 -0
- package/src/order/order-completed-template.tsx +165 -0
- package/src/order/order-confirmation-header.tsx +65 -0
- package/src/order/order-delivery-card.tsx +258 -0
- package/src/order/order-help-section.tsx +47 -0
- package/src/order/order-item.tsx +201 -0
- package/src/order/order-items-list.tsx +52 -0
- package/src/order/order-payment-card.tsx +95 -0
- package/src/order/order-timeline.tsx +141 -0
- package/src/order/order-totals.tsx +245 -0
- package/src/primitives/field.tsx +125 -0
- package/src/primitives/select-field.tsx +77 -0
- package/src/primitives/ui/accordion.tsx +61 -0
- package/src/primitives/ui/button.tsx +68 -0
- package/src/primitives/ui/collapsible.tsx +16 -0
- package/src/primitives/ui/dialog.tsx +112 -0
- package/src/primitives/ui/input.tsx +30 -0
- package/src/primitives/ui/label.tsx +31 -0
- package/src/primitives/ui/popover.tsx +38 -0
- package/src/primitives/ui/select.tsx +163 -0
- package/src/primitives/ui/sheet.tsx +131 -0
- package/src/primitives/ui/tabs.tsx +62 -0
- package/src/products/context.tsx +34 -0
- package/src/products/image-gallery.tsx +43 -0
- package/src/products/index.ts +44 -0
- package/src/products/labels-bg.ts +35 -0
- package/src/products/labels.ts +57 -0
- package/src/products/mobile-actions.tsx +180 -0
- package/src/products/option-select.tsx +67 -0
- package/src/products/preview-price.tsx +36 -0
- package/src/products/product-actions-wrapper.tsx +58 -0
- package/src/products/product-actions.tsx +217 -0
- package/src/products/product-info.tsx +43 -0
- package/src/products/product-preview.tsx +49 -0
- package/src/products/product-price.tsx +69 -0
- package/src/products/product-tabs.tsx +169 -0
- package/src/products/product-template.tsx +114 -0
- package/src/products/purchase-options.tsx +130 -0
- package/src/products/related-products.tsx +86 -0
- package/src/products/thumbnail.tsx +71 -0
- package/src/products/variant-matching.ts +71 -0
- package/src/reviews-ui/helpers.ts +174 -0
- package/src/reviews-ui/index.ts +74 -0
- package/src/reviews-ui/labels-bg.ts +91 -0
- package/src/reviews-ui/labels.ts +199 -0
- package/src/reviews-ui/photo-upload.tsx +345 -0
- package/src/reviews-ui/review-list.tsx +249 -0
- package/src/reviews-ui/review-widget.tsx +224 -0
- package/src/reviews-ui/review-wizard.tsx +560 -0
- package/src/reviews-ui/star-badge.tsx +104 -0
- package/src/reviews-ui/wizard-state.ts +81 -0
- package/src/store/category-template.tsx +129 -0
- package/src/store/collection-template.tsx +139 -0
- package/src/store/index.ts +41 -0
- package/src/store/labels-bg.ts +22 -0
- package/src/store/labels.ts +52 -0
- package/src/store/paginated-products.tsx +116 -0
- package/src/store/pagination.tsx +103 -0
- package/src/store/search-params.ts +256 -0
- package/src/store/search-template.tsx +249 -0
- package/src/store/skeleton-product-grid.tsx +26 -0
- package/src/store/sort-select.tsx +81 -0
- package/src/store/store-template.tsx +65 -0
- package/src/tracking/attribution.ts +418 -0
- package/src/tracking/consent-banner.tsx +355 -0
- package/src/tracking/consent-init.tsx +44 -0
- package/src/tracking/consent.ts +243 -0
- package/src/tracking/fbq.ts +168 -0
- package/src/tracking/ga4.tsx +49 -0
- package/src/tracking/get-tracking-attribution.ts +224 -0
- package/src/tracking/get-tracking-config.ts +50 -0
- package/src/tracking/gtag.ts +200 -0
- package/src/tracking/index.ts +133 -0
- package/src/tracking/meta-pixel.tsx +166 -0
- package/src/tracking/rybbit-events.ts +242 -0
- package/src/tracking/rybbit.tsx +40 -0
- package/src/tracking/types.ts +185 -0
- package/src/tracking/use-engagement-time.ts +58 -0
- package/tailwind-preset.cjs +72 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared tracking types — Meta Pixel + Google Analytics 4 + consent.
|
|
3
|
+
*
|
|
4
|
+
* Ported from `@1click/ui/src/tracking/types.ts` (v2.3.1) and re-seated on
|
|
5
|
+
* the barter contract:
|
|
6
|
+
*
|
|
7
|
+
* - `TrackingConfig` mirrors the barter store-config `tracking` block
|
|
8
|
+
* served by `GET /api/store/integrations` (code truth:
|
|
9
|
+
* `src/lib/tracking/constants.ts` `StoreTrackingBlock` +
|
|
10
|
+
* `src/lib/tracking/store-block.ts`; contract: store-api.md "Tracking
|
|
11
|
+
* block"). Differences vs the @1click shape: `googleAds` is a barter
|
|
12
|
+
* extension, `consent_required` is new (consent-management card), and
|
|
13
|
+
* `rybbit` is deliberately ABSENT — Rybbit is platform analytics,
|
|
14
|
+
* provisioned from platform env, never a tenant integration; the
|
|
15
|
+
* `<Rybbit>` component takes its props directly from the host app.
|
|
16
|
+
*
|
|
17
|
+
* - Server-side CAPI + GA4 Measurement Protocol sending is
|
|
18
|
+
* barter-backend-owned (the `order.placed` forwarder,
|
|
19
|
+
* `src/lib/tracking/forward.ts`) — this package fires CLIENT events
|
|
20
|
+
* only and writes attribution for the server to inherit.
|
|
21
|
+
*
|
|
22
|
+
* Purchase dedupe contract (THE invariant — admin-api.md §34,
|
|
23
|
+
* `src/lib/tracking/constants.ts:32-40`):
|
|
24
|
+
* - Meta: the backend CAPI Purchase uses
|
|
25
|
+
* `event_id = "purchase_" + order.display_id`. The client Pixel MUST
|
|
26
|
+
* pass the same value as `eventID` on `fbq('track', 'Purchase', …)` —
|
|
27
|
+
* `trackPurchase()` (fbq.ts) builds the id from `order.display_id` so
|
|
28
|
+
* the format can never drift. Meta dedupes by event_name + event_id
|
|
29
|
+
* within ~3 days.
|
|
30
|
+
* - GA4: Measurement Protocol uses
|
|
31
|
+
* `transaction_id = String(order.display_id)`; pass the same value in
|
|
32
|
+
* `trackGAPurchase()` and GA4 dedupes automatically.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
export type TrackingConfig = {
|
|
36
|
+
facebookPixel?: { pixelId: string }
|
|
37
|
+
gtm?: { containerId: string }
|
|
38
|
+
ga4?: { measurementId: string }
|
|
39
|
+
klaviyo?: { publicKey: string }
|
|
40
|
+
/** Barter extension over the @1click TrackingConfig — explicit Google
|
|
41
|
+
* Ads tag config (the @1click engine wired Ads through GTM). */
|
|
42
|
+
googleAds?: { conversionId: string; conversionLabel?: string }
|
|
43
|
+
/** True when the store's consent CMP is enabled — client tags must
|
|
44
|
+
* mount through the consent gate (`_1c_consent` / Consent Mode v2). */
|
|
45
|
+
consent_required?: boolean
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Envelope of `GET /api/store/integrations` (tracking key only — the
|
|
49
|
+
* endpoint also serves carriers/COD blocks owned by other cards). */
|
|
50
|
+
export type TrackingConfigResponse = {
|
|
51
|
+
tracking?: TrackingConfig
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** A single line item passed in `contents`. Meta requires `id` + `quantity`. */
|
|
55
|
+
export type MetaContentItem = {
|
|
56
|
+
id: string
|
|
57
|
+
quantity: number
|
|
58
|
+
item_price?: number
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type ViewContentData = {
|
|
62
|
+
content_ids: string[]
|
|
63
|
+
content_type: "product" | "product_group"
|
|
64
|
+
currency: string
|
|
65
|
+
value: number
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type AddToCartData = {
|
|
69
|
+
content_ids: string[]
|
|
70
|
+
content_type: "product" | "product_group"
|
|
71
|
+
currency: string
|
|
72
|
+
value: number
|
|
73
|
+
contents: MetaContentItem[]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export type InitiateCheckoutData = {
|
|
77
|
+
content_ids: string[]
|
|
78
|
+
content_type: "product" | "product_group"
|
|
79
|
+
currency: string
|
|
80
|
+
value: number
|
|
81
|
+
num_items: number
|
|
82
|
+
contents: MetaContentItem[]
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export type PurchaseData = {
|
|
86
|
+
content_ids: string[]
|
|
87
|
+
content_type: "product" | "product_group"
|
|
88
|
+
currency: string
|
|
89
|
+
value: number
|
|
90
|
+
num_items: number
|
|
91
|
+
contents: MetaContentItem[]
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Lead event payload — shape per Meta's Lead standard event spec.
|
|
96
|
+
* All fields optional (Meta accepts an empty Lead). Storefront wraps
|
|
97
|
+
* popup signups + newsletter forms in a Lead event so Meta can
|
|
98
|
+
* optimize ad delivery for "likely subscribers" and build Lead
|
|
99
|
+
* lookalike audiences from our funnel.
|
|
100
|
+
*/
|
|
101
|
+
export type LeadData = {
|
|
102
|
+
/** Optional lead value (e.g. expected LTV for a subscriber). */
|
|
103
|
+
value?: number
|
|
104
|
+
currency?: string
|
|
105
|
+
/** Free-text label describing what the visitor signed up for —
|
|
106
|
+
* e.g. "newsletter_popup", "newsletter_footer". */
|
|
107
|
+
content_name?: string
|
|
108
|
+
content_category?: string
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Attribution payload written to `cart.metadata` before completion
|
|
113
|
+
* (consent-gated — see store-api.md "Tracking block" wiring contract).
|
|
114
|
+
* Cart completion copies these keys onto `order.metadata` and the barter
|
|
115
|
+
* `order.placed` forwarder reads exactly them to build the CAPI Purchase
|
|
116
|
+
* `user_data` and the GA4 MP Purchase `client_id` / `session_id`
|
|
117
|
+
* (code truth: `src/lib/tracking/constants.ts` TrackingAttributionMeta).
|
|
118
|
+
*
|
|
119
|
+
* - fb_fbp / fb_fbc: from _fbp / _fbc cookies (set by fbq init, or
|
|
120
|
+
* defensively by attribution.ts before the Pixel loads)
|
|
121
|
+
* - fb_user_agent: from request User-Agent header
|
|
122
|
+
* - fb_client_ip: from request x-forwarded-for / x-real-ip
|
|
123
|
+
* - fb_event_source_url: from request Referer
|
|
124
|
+
* - ga_client_id: from _ga cookie value, schema prefix stripped
|
|
125
|
+
* - ga_session_id: parsed from _ga_<MEASUREMENT_ID> cookie's session segment
|
|
126
|
+
* - ga_engagement_time_msec: client-tracked time-on-session in ms
|
|
127
|
+
* (passed in via clientHints on order placement, not derivable server-side)
|
|
128
|
+
*/
|
|
129
|
+
export type TrackingAttribution = {
|
|
130
|
+
fb_fbp?: string
|
|
131
|
+
fb_fbc?: string
|
|
132
|
+
fb_user_agent?: string
|
|
133
|
+
fb_client_ip?: string
|
|
134
|
+
fb_event_source_url?: string
|
|
135
|
+
/** Per-browser anonymous visitor id (cookie `_1c_anon`). Backend
|
|
136
|
+
* uses this as `external_id` on the Purchase CAPI when no
|
|
137
|
+
* customer_id is available, AND alongside customer_id when both
|
|
138
|
+
* are known (Meta accepts external_id as an array, so dedup +
|
|
139
|
+
* cross-session attribution both improve). */
|
|
140
|
+
fb_anon_id?: string
|
|
141
|
+
ga_client_id?: string
|
|
142
|
+
ga_session_id?: string
|
|
143
|
+
ga_engagement_time_msec?: number
|
|
144
|
+
|
|
145
|
+
// ── UTM attribution ────────────────────────────────────────────────
|
|
146
|
+
// Captured by browser-side `captureUtmsFromUrl()` (attribution.ts)
|
|
147
|
+
// into `_1c_utm_first` / `_1c_utm_last` cookies, then read here at
|
|
148
|
+
// checkout completion. Flat keys (instead of nested objects) so a
|
|
149
|
+
// raw `SELECT metadata->>'utm_first_source' FROM orders` query
|
|
150
|
+
// works without `jsonb_path_query`. Each tuple's `captured_at` is
|
|
151
|
+
// Unix seconds — `attribution_window_days = (order.created_at_unix
|
|
152
|
+
// - captured_at) / 86400`.
|
|
153
|
+
|
|
154
|
+
/** Acquisition (first-touch) UTM source — the campaign that
|
|
155
|
+
* originally brought this visitor in. 365-day TTL. Stable across
|
|
156
|
+
* multiple ad-click revisits. */
|
|
157
|
+
utm_first_source?: string
|
|
158
|
+
utm_first_medium?: string
|
|
159
|
+
utm_first_campaign?: string
|
|
160
|
+
utm_first_term?: string
|
|
161
|
+
utm_first_content?: string
|
|
162
|
+
utm_first_captured_at?: number
|
|
163
|
+
|
|
164
|
+
/** Closer (last-touch) UTM source — the campaign whose click
|
|
165
|
+
* immediately preceded this order. 90-day TTL, refreshed on every
|
|
166
|
+
* UTM-bearing visit. Aligns with Meta / Google Ads attribution
|
|
167
|
+
* windows. */
|
|
168
|
+
utm_last_source?: string
|
|
169
|
+
utm_last_medium?: string
|
|
170
|
+
utm_last_campaign?: string
|
|
171
|
+
utm_last_term?: string
|
|
172
|
+
utm_last_content?: string
|
|
173
|
+
utm_last_captured_at?: number
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Client-only attribution data — must be passed through to order
|
|
178
|
+
* placement by the storefront because it can't be read from
|
|
179
|
+
* cookies/headers on the server (the browser computes it during the
|
|
180
|
+
* session).
|
|
181
|
+
*/
|
|
182
|
+
export type TrackingClientHints = {
|
|
183
|
+
/** Accumulated time-on-session in ms (Date.now() - sessionStart). */
|
|
184
|
+
engagementTimeMsec?: number
|
|
185
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Engagement-time helpers (client only).
|
|
5
|
+
*
|
|
6
|
+
* The GA4 Measurement Protocol Purchase event needs
|
|
7
|
+
* `ga_engagement_time_msec` — how long the user spent in the session
|
|
8
|
+
* before placing the order. This can't be read from a cookie; it has
|
|
9
|
+
* to be tracked by the browser.
|
|
10
|
+
*
|
|
11
|
+
* Strategy: store `sessionStartTs` in `sessionStorage` on first read.
|
|
12
|
+
* On every subsequent call, return `Date.now() - sessionStartTs`.
|
|
13
|
+
* sessionStorage scopes to the tab — closing the tab resets the
|
|
14
|
+
* session, which is the desired behaviour.
|
|
15
|
+
*
|
|
16
|
+
* Cap at 30 minutes to filter out idle/dead-tab sessions. GA4's own
|
|
17
|
+
* session timeout default is 30 minutes, so this matches.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const SESSION_KEY = "tracking_session_start"
|
|
21
|
+
const MAX_ENGAGEMENT_MS = 30 * 60 * 1000 // 30 minutes
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Returns the current engagement time in milliseconds for this session.
|
|
25
|
+
* Returns 0 in non-browser contexts. Always returns a number ≥ 0.
|
|
26
|
+
*/
|
|
27
|
+
export function getEngagementTimeMsec(): number {
|
|
28
|
+
if (typeof window === "undefined") return 0
|
|
29
|
+
try {
|
|
30
|
+
let start = window.sessionStorage.getItem(SESSION_KEY)
|
|
31
|
+
if (!start) {
|
|
32
|
+
start = String(Date.now())
|
|
33
|
+
window.sessionStorage.setItem(SESSION_KEY, start)
|
|
34
|
+
}
|
|
35
|
+
const elapsed = Date.now() - Number(start)
|
|
36
|
+
if (!Number.isFinite(elapsed) || elapsed < 0) return 0
|
|
37
|
+
return Math.min(elapsed, MAX_ENGAGEMENT_MS)
|
|
38
|
+
} catch {
|
|
39
|
+
return 0
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Initialise the session start timestamp now (no return value). Call
|
|
45
|
+
* from the root layout's MetaPixel/GA4 wrapper so the timer starts on
|
|
46
|
+
* first page view of the session, not on first checkout.
|
|
47
|
+
*/
|
|
48
|
+
export function initEngagementTime(): void {
|
|
49
|
+
if (typeof window === "undefined") return
|
|
50
|
+
try {
|
|
51
|
+
if (!window.sessionStorage.getItem(SESSION_KEY)) {
|
|
52
|
+
window.sessionStorage.setItem(SESSION_KEY, String(Date.now()))
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
// sessionStorage unavailable — Safari private mode etc. Engagement
|
|
56
|
+
// time will be 0 in that case, which the backend treats as "unknown".
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @cartbase/storefront Tailwind preset — shadcn/ui standard token structure
|
|
3
|
+
* (ported verbatim from @1click/ui tailwind-preset.cjs v2.3.1)
|
|
4
|
+
*
|
|
5
|
+
* Uses oklch color space (modern CSS standard, matches shadcn/ui Create output).
|
|
6
|
+
* CSS variables store raw oklch channels so Tailwind's opacity modifiers work:
|
|
7
|
+
* --primary: 0.553 0.195 38.402;
|
|
8
|
+
* → bg-primary = oklch(0.553 0.195 38.402)
|
|
9
|
+
* → bg-primary/50 = oklch(0.553 0.195 38.402 / 50%)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** @type {import('tailwindcss').Config} */
|
|
13
|
+
module.exports = {
|
|
14
|
+
theme: {
|
|
15
|
+
extend: {
|
|
16
|
+
colors: {
|
|
17
|
+
background: "oklch(var(--background) / <alpha-value>)",
|
|
18
|
+
foreground: "oklch(var(--foreground) / <alpha-value>)",
|
|
19
|
+
card: {
|
|
20
|
+
DEFAULT: "oklch(var(--card) / <alpha-value>)",
|
|
21
|
+
foreground: "oklch(var(--card-foreground) / <alpha-value>)",
|
|
22
|
+
},
|
|
23
|
+
popover: {
|
|
24
|
+
DEFAULT: "oklch(var(--popover) / <alpha-value>)",
|
|
25
|
+
foreground: "oklch(var(--popover-foreground) / <alpha-value>)",
|
|
26
|
+
},
|
|
27
|
+
primary: {
|
|
28
|
+
DEFAULT: "oklch(var(--primary) / <alpha-value>)",
|
|
29
|
+
foreground: "oklch(var(--primary-foreground) / <alpha-value>)",
|
|
30
|
+
},
|
|
31
|
+
secondary: {
|
|
32
|
+
DEFAULT: "oklch(var(--secondary) / <alpha-value>)",
|
|
33
|
+
foreground: "oklch(var(--secondary-foreground) / <alpha-value>)",
|
|
34
|
+
},
|
|
35
|
+
muted: {
|
|
36
|
+
DEFAULT: "oklch(var(--muted) / <alpha-value>)",
|
|
37
|
+
foreground: "oklch(var(--muted-foreground) / <alpha-value>)",
|
|
38
|
+
},
|
|
39
|
+
accent: {
|
|
40
|
+
DEFAULT: "oklch(var(--accent) / <alpha-value>)",
|
|
41
|
+
foreground: "oklch(var(--accent-foreground) / <alpha-value>)",
|
|
42
|
+
},
|
|
43
|
+
destructive: {
|
|
44
|
+
DEFAULT: "oklch(var(--destructive) / <alpha-value>)",
|
|
45
|
+
foreground: "oklch(var(--destructive-foreground) / <alpha-value>)",
|
|
46
|
+
},
|
|
47
|
+
border: "oklch(var(--border) / <alpha-value>)",
|
|
48
|
+
input: "oklch(var(--input) / <alpha-value>)",
|
|
49
|
+
ring: "oklch(var(--ring) / <alpha-value>)",
|
|
50
|
+
success: {
|
|
51
|
+
DEFAULT: "oklch(var(--success) / <alpha-value>)",
|
|
52
|
+
foreground: "oklch(var(--success-foreground) / <alpha-value>)",
|
|
53
|
+
},
|
|
54
|
+
warning: {
|
|
55
|
+
DEFAULT: "oklch(var(--warning) / <alpha-value>)",
|
|
56
|
+
foreground: "oklch(var(--warning-foreground) / <alpha-value>)",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
fontFamily: {
|
|
60
|
+
display: ["var(--font-display)", "serif"],
|
|
61
|
+
body: ["var(--font-body)", "system-ui", "sans-serif"],
|
|
62
|
+
mono: ["var(--font-mono)", "ui-monospace", "monospace"],
|
|
63
|
+
},
|
|
64
|
+
borderRadius: {
|
|
65
|
+
lg: "var(--radius)",
|
|
66
|
+
md: "calc(var(--radius) - 2px)",
|
|
67
|
+
sm: "calc(var(--radius) - 4px)",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
plugins: [],
|
|
72
|
+
}
|