create-brainerce-store 1.52.2 → 1.52.5

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/dist/index.js CHANGED
@@ -31,7 +31,7 @@ var require_package = __commonJS({
31
31
  "package.json"(exports2, module2) {
32
32
  module2.exports = {
33
33
  name: "create-brainerce-store",
34
- version: "1.52.2",
34
+ version: "1.52.5",
35
35
  description: "Scaffold a production-ready e-commerce storefront connected to Brainerce",
36
36
  bin: {
37
37
  "create-brainerce-store": "dist/index.js"
@@ -393,6 +393,9 @@ async function scaffold(options) {
393
393
  const templateVars = {
394
394
  projectName: options.projectName,
395
395
  connectionId: options.connectionId,
396
+ /** Raw store name for prose contexts (markdown docs) — JS/env contexts
397
+ * must keep using the escaped storeNameJs / storeNameEnv variants. */
398
+ storeName: cleanStoreName,
396
399
  storeNameJs: toJsStringLiteral(cleanStoreName),
397
400
  titleTemplateJs: toJsStringLiteral(`%s | ${cleanStoreName}`),
398
401
  storeNameEnv: toEnvLiteral(cleanStoreName),
@@ -969,7 +972,9 @@ live in messages/ (or let an AI session rewrite them).`
969
972
  );
970
973
  logger.info(
971
974
  `
972
- Want a unique design? Open this folder in Claude Code and run:
975
+ Want a unique design? Open THE STORE FOLDER ITSELF in Claude Code
976
+ (not a parent folder \u2014 that's how CLAUDE.md auto-loads and the AI
977
+ knows this is a live, connected Brainerce storefront), then run:
973
978
  /design <your art-direction brief>
974
979
  (commerce stays intact \u2014 see AI-GUIDE.md)`
975
980
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-brainerce-store",
3
- "version": "1.52.2",
3
+ "version": "1.52.5",
4
4
  "description": "Scaffold a production-ready e-commerce storefront connected to Brainerce",
5
5
  "bin": {
6
6
  "create-brainerce-store": "dist/index.js"
@@ -0,0 +1,65 @@
1
+ # AGENTS.md — this is a LIVE Brainerce storefront
2
+
3
+ **Store: "<%- storeName %>" · sales channel `<%- connectionId %>` — already
4
+ connected.** Products, cart, checkout, coupons, discounts, orders and content
5
+ flow in real time from the Brainerce dashboard. There is nothing to hook up.
6
+
7
+ - **NEVER suggest connecting this store to Shopify, WooCommerce, or "a real
8
+ system"** — Brainerce IS the commerce backend, and this store is wired to
9
+ it end-to-end.
10
+ - **NEVER build standalone HTML mockups or demo pages** — design THIS Next.js
11
+ app. Run `pnpm dev` and you are working against live data.
12
+ - **NEVER hardcode products, prices, or currency** — the catalog is live.
13
+ - **No server side is needed** — the backend is Brainerce's cloud. This repo
14
+ is a frontend (plus thin, already-included API proxy routes under
15
+ `src/app/api/`). Do not scaffold databases, auth servers, or admin panels —
16
+ the merchant manages everything in the Brainerce dashboard.
17
+
18
+ Platform docs (endpoints, SDK, integration recipes): https://brainerce.com/docs
19
+ — AI-readable index: https://brainerce.com/llms.txt
20
+
21
+ **Before building any feature the merchant asks for** (loyalty points,
22
+ shipping zones, subscriptions, gift cards, multi-currency, reviews,
23
+ abandoned-cart recovery, etc.) — check the docs first. Brainerce likely
24
+ already has it as a platform capability (dashboard toggle + hook/SDK
25
+ method) that only needs a UI in `src/ui/`, not a feature built from scratch.
26
+
27
+ Your job here is almost always **design**.
28
+
29
+ ## The one rule
30
+
31
+ **`src/core/` is the platform's. `src/ui/` is yours.**
32
+
33
+ The shipped `src/ui/` is a working reference, not a design to preserve —
34
+ full delete-and-rebuild of ui files is encouraged and expected.
35
+ Rewrite anything under `src/ui/` and `src/app/globals.css` as boldly as you
36
+ like — the store keeps working. Never modify `src/core/`, `src/app/api/`, or
37
+ the checkout/auth/account components. Data and behavior come exclusively from
38
+ `@/core/hooks/*` and `@/core/providers/store-provider` — hooks return state
39
+ and handlers, never JSX. Never hardcode catalog content.
40
+
41
+ **Read `AI-GUIDE.md` before any redesign** — it has the full file map, hook
42
+ contracts, motion language, and hard-won RTL/i18n gotchas that will save you
43
+ real debugging time.
44
+
45
+ ## Redesigning?
46
+
47
+ Follow the process documented in AI-GUIDE.md: commit to one art direction,
48
+ rewrite tokens first, then surfaces in order (header, home, card, product
49
+ page, cart), then verify.
50
+
51
+ ## Verify before declaring done
52
+
53
+ 1. `pnpm exec tsc --noEmit` → 0 errors
54
+ 2. `pnpm dev` → drive the changed flow in a real browser (home → product →
55
+ add to cart → cart)
56
+ 3. Screenshot desktop (1440px) and mobile (390px)
57
+ 4. RTL stores: check anchoring and arrow directions
58
+
59
+ ## i18n
60
+
61
+ Every user-facing string goes through `useTranslations()` with keys in **all**
62
+ files under `messages/`. The shipped copy is example boutique content — a
63
+ starting point meant to be rewritten in the store's real voice. Hebrew: no
64
+ uppercase transforms, no wide letter-spacing on headings, logical CSS
65
+ properties only (`ms-/me-`, `ps-/pe-`, `start-/end-`).
@@ -103,6 +103,37 @@ Beautiful is the top priority — the first render must wow. Hard rules:
103
103
  it ("would a Lovable user accept this?"), fix, and only then move on.
104
104
 
105
105
 
106
+ ## Motion language (CSS-first)
107
+
108
+ The store should feel alive, premium and intentional — every animation guides
109
+ attention, confirms an action, or rewards exploration. Rules:
110
+
111
+ - **Timing tokens** (use these, never ad-hoc numbers): 80ms instant feedback ·
112
+ 180ms hover · 300ms UI state change · 500-600ms section entrance · 900ms
113
+ hero only. Stagger siblings 40-80ms.
114
+ - **Easing**: entrances `cubic-bezier(0.16, 1, 0.3, 1)` (out-expo); springy
115
+ pops `cubic-bezier(0.34, 1.56, 0.64, 1)` (out-back); loops and hovers
116
+ ease-in-out. Exits faster than entrances.
117
+ - **Reveals**: below-the-fold sections animate on viewport entry
118
+ (IntersectionObserver — see `src/ui/shared/reveal.tsx`), never tied to
119
+ scroll distance. One orchestrated reveal per section beats scattered
120
+ effects; `viewport once` — do not re-animate on every scroll.
121
+ - **Micro-interactions**: CSS/WAAPI first (see `src/ui/shared/fly-to-cart.ts`
122
+ for the pattern) — ripple, bump, image zoom, quick-add slide-up are all
123
+ achievable without a library.
124
+ - **Pointer effects** (magnetic buttons, tilt cards, custom cursor, mouse
125
+ parallax): desktop only — gate on `(pointer: fine)` and no-op under
126
+ `prefers-reduced-motion`. `will-change: transform` ONLY on elements animated
127
+ per-frame; never sprinkle it broadly.
128
+ - **Never `transition-all`** — always name the property
129
+ (`transition-transform`, `transition-opacity`). Animate only compositor
130
+ properties (`transform`, `opacity`) — never layout properties
131
+ (width/height/top/margin). 60fps is non-negotiable.
132
+ - **Ambient effects** (gradient blobs, particles): maximum one per page, hero
133
+ only — never on inner pages.
134
+ - **framer-motion / GSAP are not installed.** They are allowed, but they are
135
+ new runtime dependencies — flag it to the user before adding.
136
+
106
137
  ## Hard-won gotchas (each one cost a real debugging session)
107
138
 
108
139
  - **RTL + cloned images**: `next/image` renders its `<img>` with `inset: 0`.
@@ -0,0 +1,76 @@
1
+ # CLAUDE.md — this is a LIVE Brainerce storefront
2
+
3
+ **Store: "<%- storeName %>" · sales channel `<%- connectionId %>` — already
4
+ connected.** Products, cart, checkout, coupons, discounts, orders and content
5
+ flow in real time from the Brainerce dashboard. There is nothing to hook up.
6
+
7
+ - **NEVER suggest connecting this store to Shopify, WooCommerce, or "a real
8
+ system"** — Brainerce IS the commerce backend, and this store is wired to
9
+ it end-to-end.
10
+ - **NEVER build standalone HTML mockups or demo pages** — design THIS Next.js
11
+ app. Run `pnpm dev` and you are working against live data.
12
+ - **NEVER hardcode products, prices, or currency** — the catalog is live.
13
+ - **No server side is needed** — the backend is Brainerce's cloud. This repo
14
+ is a frontend (plus thin, already-included API proxy routes under
15
+ `src/app/api/`). Do not scaffold databases, auth servers, or admin panels —
16
+ the merchant manages everything in the Brainerce dashboard.
17
+
18
+ Platform docs (endpoints, SDK, integration recipes): https://brainerce.com/docs
19
+ — AI-readable index: https://brainerce.com/llms.txt
20
+
21
+ **Before building any feature the merchant asks for** (loyalty points,
22
+ shipping zones, subscriptions, gift cards, multi-currency, reviews,
23
+ abandoned-cart recovery, etc.) — check the docs first. Brainerce likely
24
+ already has it as a platform capability (dashboard toggle + hook/SDK
25
+ method) that only needs a UI in `src/ui/`, not a feature built from scratch.
26
+
27
+ Your job here is almost always **design**.
28
+
29
+ ## The one rule
30
+
31
+ **`src/core/` is the platform's. `src/ui/` is yours.**
32
+
33
+ The shipped `src/ui/` is a working reference, not a design to preserve —
34
+ full delete-and-rebuild of ui files is encouraged and expected. (Stores
35
+ scaffolded with `--canvas` ship `src/ui/` as bare unstyled skeletons —
36
+ there is no reference look at all; the design is entirely yours to create.)
37
+ Rewrite anything under `src/ui/` and `src/app/globals.css` as boldly as you
38
+ like — the store keeps working. Never modify `src/core/`, `src/app/api/`, or
39
+ the checkout/auth/account components. Data and behavior come exclusively from
40
+ `@/core/hooks/*` and `@/core/providers/store-provider` — hooks return state
41
+ and handlers, never JSX. Never hardcode catalog content.
42
+
43
+ **Read `AI-GUIDE.md` before any redesign** — it has the full file map, hook
44
+ contracts, motion language, and hard-won RTL/i18n gotchas that will save you
45
+ real debugging time.
46
+
47
+ ## The dialect
48
+
49
+ - `src/components/ui/` = official **shadcn/ui primitives** (Button, Card,
50
+ Badge, Input, Select, Accordion, Dialog, Sheet, Skeleton, …). Compose them;
51
+ extend looks via CVA **variants**, never repeated inline overrides.
52
+ - Icons: **lucide-react only** — no inline `<svg>`, no emoji-as-icons.
53
+ - Class merging: **`cn()`** from `@/core/lib/utils`.
54
+ - Colors/radius come from the semantic HSL tokens in `globals.css`
55
+ (`--primary`, `--card`, `--ring`, `--radius`, …) — restyle via tokens.
56
+
57
+ ## Redesigning? Use the ready-made flow
58
+
59
+ Run `/design <your art-direction brief>` — it walks the whole process:
60
+ concept → implementation in `ui/` → verification.
61
+
62
+ ## Verify before declaring done
63
+
64
+ 1. `pnpm exec tsc --noEmit` → 0 errors
65
+ 2. `pnpm dev` → drive the changed flow in a real browser (home → product →
66
+ add to cart → cart)
67
+ 3. Screenshot desktop (1440px) and mobile (390px)
68
+ 4. RTL stores: check anchoring and arrow directions
69
+
70
+ ## i18n
71
+
72
+ Every user-facing string goes through `useTranslations()` with keys in **all**
73
+ files under `messages/`. The shipped copy is example boutique content — a
74
+ starting point meant to be rewritten in the store's real voice. Hebrew: no
75
+ uppercase transforms, no wide letter-spacing on headings, logical CSS
76
+ properties only (`ms-/me-`, `ps-/pe-`, `start-/end-`).
@@ -26,7 +26,17 @@ const nextConfig: NextConfig = {
26
26
  // isomorphic-dompurify ships jsdom, which at runtime reads stylesheet files
27
27
  // from its own package directory. Webpack bundling breaks those relative
28
28
  // lookups — loading it externally from node_modules keeps the paths intact.
29
- serverExternalPackages: ['isomorphic-dompurify'],
29
+ //
30
+ // brainerce (the SDK) is imported across both the server and client
31
+ // compilation boundaries in ~60 files, including the [locale]/layout.tsx
32
+ // route (see generateStaticParams below) whose static params are resolved
33
+ // by a separate jest-worker child process in `next dev` (webpack). That
34
+ // worker requires the compiled page against the vendor-chunks manifest
35
+ // while the main dev server may still be writing it, which can throw
36
+ // "Cannot find module './vendor-chunks/brainerce.js'". Excluding it from
37
+ // server bundling (loaded externally from node_modules instead) avoids the
38
+ // race.
39
+ serverExternalPackages: ['isomorphic-dompurify', 'brainerce'],
30
40
  images: {
31
41
  // The storefront is a consumer of the Brainerce API — it has to render
32
42
  // whatever image URLs the API returns. In practice those URLs are
@@ -7,7 +7,7 @@ import { SiteHeader } from '@/ui/layout/site-header';
7
7
  import { SiteFooter } from '@/ui/layout/site-footer';
8
8
  import { BrainerceBotWidget } from '@/components/brainerce-bot';
9
9
  import { getServerClient, fetchStoreInfo } from '@/core/lib/brainerce';
10
- import { getDirection, supportedLocales } from '@/i18n';
10
+ import { getDirection, getMessages, supportedLocales } from '@/i18n';
11
11
  import { getNonce } from '@/core/lib/nonce';
12
12
  import '../globals.css';
13
13
 
@@ -62,7 +62,7 @@ export default async function RootLayout({
62
62
  // seeded a particular content type yet. New stores ship with default rows
63
63
  // seeded by the backend (StoresService.seedDefaultContent).
64
64
  const client = getServerClient(locale);
65
- const [announcements, siteHeader, siteFooter, storeInfo] = await Promise.all([
65
+ const [announcements, siteHeader, siteFooter, storeInfo, messages] = await Promise.all([
66
66
  client.content.announcement.list(locale).catch(() => []),
67
67
  client.content.header.get('main', locale).catch(() => null),
68
68
  client.content.footer.get('main', locale).catch(() => null),
@@ -70,6 +70,10 @@ export default async function RootLayout({
70
70
  // render with the real currency, feature flags, and i18n config at frame 0
71
71
  // — without this, Googlebot sees USD/defaults baked into the HTML.
72
72
  fetchStoreInfo(locale),
73
+ // SSR-fetch UI message strings so translated text renders at frame 0 —
74
+ // without this, every page (and every locale switch) shows raw
75
+ // "namespace.key" strings until the client-only fetch resolves.
76
+ getMessages(locale),
73
77
  ]);
74
78
 
75
79
  return (
@@ -100,7 +104,7 @@ export default async function RootLayout({
100
104
  />
101
105
  </head>
102
106
  <body className={font.className}>
103
- <StoreProvider locale={locale} initialStoreInfo={storeInfo}>
107
+ <StoreProvider locale={locale} initialStoreInfo={storeInfo} initialMessages={messages}>
104
108
  <div className="min-h-screen flex flex-col">
105
109
  <AnnouncementBar announcements={announcements} />
106
110
  <SiteHeader header={siteHeader} storeName={<%- storeNameJs %>} />
@@ -74,10 +74,12 @@ export function StoreProvider({
74
74
  children,
75
75
  locale,
76
76
  initialStoreInfo = null,
77
+ initialMessages,
77
78
  }: {
78
79
  children: React.ReactNode;
79
80
  locale?: string;
80
81
  initialStoreInfo?: PublicStoreInfo | null;
82
+ initialMessages?: Record<string, Record<string, string>>;
81
83
  }) {
82
84
  <% } else { %>
83
85
  export function StoreProvider({
@@ -99,7 +101,14 @@ export function StoreProvider({
99
101
  const [cart, setCart] = useState<Cart | null>(null);
100
102
  const [cartLoading, setCartLoading] = useState(true);
101
103
  <% if (i18nEnabled) { %>
102
- const [messages, setMessages] = useState<Record<string, Record<string, string>>>({});
104
+ // Seed from server-rendered messages (see RootLayout) so translated text
105
+ // renders at frame 0 instead of a window of raw "namespace.key" strings
106
+ // before the client-only fetch in the effect below resolves — this also
107
+ // closes the same gap on every locale switch, since RootLayout re-runs
108
+ // server-side per locale and StoreProvider remounts with fresh initial data.
109
+ const [messages, setMessages] = useState<Record<string, Record<string, string>>>(
110
+ initialMessages ?? {}
111
+ );
103
112
  <% } %>
104
113
 
105
114
  // Check auth status via httpOnly cookie (server-side validation)
@@ -8,7 +8,7 @@ import { SiteFooter } from '@/ui/layout/site-footer';
8
8
  import { CartDrawer } from '@/ui/cart/cart-drawer';
9
9
  import { BrainerceBotWidget } from '@/components/brainerce-bot';
10
10
  import { getServerClient, fetchStoreInfo } from '@/core/lib/brainerce';
11
- import { getDirection, supportedLocales } from '@/i18n';
11
+ import { getDirection, getMessages, supportedLocales } from '@/i18n';
12
12
  import { getNonce } from '@/core/lib/nonce';
13
13
  import '../globals.css';
14
14
 
@@ -75,7 +75,7 @@ export default async function RootLayout({
75
75
  // seeded a particular content type yet. New stores ship with default rows
76
76
  // seeded by the backend (StoresService.seedDefaultContent).
77
77
  const client = getServerClient(locale);
78
- const [announcements, siteHeader, siteFooter, storeInfo] = await Promise.all([
78
+ const [announcements, siteHeader, siteFooter, storeInfo, messages] = await Promise.all([
79
79
  client.content.announcement.list(locale).catch(() => []),
80
80
  client.content.header.get('main', locale).catch(() => null),
81
81
  client.content.footer.get('main', locale).catch(() => null),
@@ -83,6 +83,10 @@ export default async function RootLayout({
83
83
  // render with the real currency, feature flags, and i18n config at frame 0
84
84
  // — without this, Googlebot sees USD/defaults baked into the HTML.
85
85
  fetchStoreInfo(locale),
86
+ // SSR-fetch UI message strings so translated text renders at frame 0 —
87
+ // without this, every page (and every locale switch) shows raw
88
+ // "namespace.key" strings until the client-only fetch resolves.
89
+ getMessages(locale),
86
90
  ]);
87
91
 
88
92
  return (
@@ -113,7 +117,7 @@ export default async function RootLayout({
113
117
  />
114
118
  </head>
115
119
  <body className={`${sans.variable} ${serif.variable} font-sans`}>
116
- <StoreProvider locale={locale} initialStoreInfo={storeInfo}>
120
+ <StoreProvider locale={locale} initialStoreInfo={storeInfo} initialMessages={messages}>
117
121
  <div className="min-h-screen flex flex-col">
118
122
  <AnnouncementBar announcements={announcements} />
119
123
  <SiteHeader header={siteHeader} storeName={<%- storeNameJs %>} />
@@ -7,7 +7,7 @@
7
7
  import * as React from 'react';
8
8
 
9
9
  export type IconProps = {
10
- size?: 16 | 18 | 20 | 24 | 28 | 32;
10
+ size?: 16 | 18 | 20 | 24 | 28 | 32 | 56;
11
11
  className?: string;
12
12
  /** stroke width — 1.75 default matches the light, jewelry-grade look */
13
13
  strokeWidth?: number;
@@ -1,41 +0,0 @@
1
- # AGENTS.md — this is a Brainerce storefront
2
-
3
- This store's commerce engine (cart, checkout, variants, auth, i18n) is
4
- already wired and working. Your job here is almost always **design**.
5
-
6
- ## The one rule
7
-
8
- **`src/core/` is the platform's. `src/ui/` is yours.**
9
-
10
- The shipped `src/ui/` is a working reference, not a design to preserve —
11
- full delete-and-rebuild of ui files is encouraged and expected.
12
- Rewrite anything under `src/ui/` and `src/app/globals.css` as boldly as you
13
- like — the store keeps working. Never modify `src/core/`, `src/app/api/`, or
14
- the checkout/auth/account components. Data and behavior come exclusively from
15
- `@/core/hooks/*` and `@/core/providers/store-provider` — hooks return state
16
- and handlers, never JSX. Never hardcode catalog content.
17
-
18
- **Read `AI-GUIDE.md` before any redesign** — it has the full file map, hook
19
- contracts, and hard-won RTL/i18n gotchas that will save you real debugging
20
- time.
21
-
22
- ## Redesigning?
23
-
24
- Follow the process documented in AI-GUIDE.md: commit to one art direction,
25
- rewrite tokens first, then surfaces in order (header, home, card, product
26
- page, cart), then verify.
27
-
28
- ## Verify before declaring done
29
-
30
- 1. `pnpm exec tsc --noEmit` → 0 errors
31
- 2. `pnpm dev` → drive the changed flow in a real browser (home → product →
32
- add to cart → cart)
33
- 3. Screenshot desktop (1440px) and mobile (390px)
34
- 4. RTL stores: check anchoring and arrow directions
35
-
36
- ## i18n
37
-
38
- Every user-facing string goes through `useTranslations()` with keys in **all**
39
- files under `messages/`. Hebrew: no uppercase transforms, no wide
40
- letter-spacing on headings, logical CSS properties only (`ms-/me-`, `ps-/pe-`,
41
- `start-/end-`).
@@ -1,52 +0,0 @@
1
- # CLAUDE.md — this is a Brainerce storefront
2
-
3
- This store's commerce engine (cart, checkout, variants, auth, i18n) is
4
- already wired and working. Your job here is almost always **design**.
5
-
6
- ## The one rule
7
-
8
- **`src/core/` is the platform's. `src/ui/` is yours.**
9
-
10
- The shipped `src/ui/` is a working reference, not a design to preserve —
11
- full delete-and-rebuild of ui files is encouraged and expected. (Stores
12
- scaffolded with `--canvas` ship `src/ui/` as bare unstyled skeletons —
13
- there is no reference look at all; the design is entirely yours to create.)
14
- Rewrite anything under `src/ui/` and `src/app/globals.css` as boldly as you
15
- like — the store keeps working. Never modify `src/core/`, `src/app/api/`, or
16
- the checkout/auth/account components. Data and behavior come exclusively from
17
- `@/core/hooks/*` and `@/core/providers/store-provider` — hooks return state
18
- and handlers, never JSX. Never hardcode catalog content.
19
-
20
- **Read `AI-GUIDE.md` before any redesign** — it has the full file map, hook
21
- contracts, and hard-won RTL/i18n gotchas that will save you real debugging
22
- time.
23
-
24
- ## The dialect
25
-
26
- - `src/components/ui/` = official **shadcn/ui primitives** (Button, Card,
27
- Badge, Input, Select, Accordion, Dialog, Sheet, Skeleton, …). Compose them;
28
- extend looks via CVA **variants**, never repeated inline overrides.
29
- - Icons: **lucide-react only** — no inline `<svg>`, no emoji-as-icons.
30
- - Class merging: **`cn()`** from `@/core/lib/utils`.
31
- - Colors/radius come from the semantic HSL tokens in `globals.css`
32
- (`--primary`, `--card`, `--ring`, `--radius`, …) — restyle via tokens.
33
-
34
- ## Redesigning? Use the ready-made flow
35
-
36
- Run `/design <your art-direction brief>` — it walks the whole process:
37
- concept → implementation in `ui/` → verification.
38
-
39
- ## Verify before declaring done
40
-
41
- 1. `pnpm exec tsc --noEmit` → 0 errors
42
- 2. `pnpm dev` → drive the changed flow in a real browser (home → product →
43
- add to cart → cart)
44
- 3. Screenshot desktop (1440px) and mobile (390px)
45
- 4. RTL stores: check anchoring and arrow directions
46
-
47
- ## i18n
48
-
49
- Every user-facing string goes through `useTranslations()` with keys in **all**
50
- files under `messages/`. Hebrew: no uppercase transforms, no wide
51
- letter-spacing on headings, logical CSS properties only (`ms-/me-`, `ps-/pe-`,
52
- `start-/end-`).