apex-ui-kit 2.12.2 → 2.14.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,105 @@
1
+ # Premium motion
2
+
3
+ Motion is what turns a clean page into a premium one. Not more animation: the right few moments, done smoothly. Every premium site gets the **core set**, plus **one signature moment** chosen for the brand.
4
+
5
+ Rules that never change:
6
+ - Animate only `transform`, `opacity` and `clip-path`. Never `width`, `height`, `top`, `left` or `filter` on large elements.
7
+ - Easing: `cubic-bezier(0.16, 1, 0.3, 1)` (calm and expensive). Durations 0.8 to 1.3s for reveals, 0.4 to 0.6s for hovers.
8
+ - Play reveals **once**, when the element is about 10% into the viewport. Never re-animate on scroll back.
9
+ - Respect reduced motion: show everything at rest, keep simple fades at most.
10
+ - No bounce, no spin, no shake, no rainbow, no gradients.
11
+
12
+ The snippets use Framer Motion (`npm i framer-motion`) in React. Plain HTML: the same ideas work with an IntersectionObserver that adds a class, plus CSS transitions.
13
+
14
+ ## Core set (use on every site)
15
+
16
+ ### 1. Reveal: fade and rise
17
+ ```jsx
18
+ import { motion, useReducedMotion } from "framer-motion";
19
+ const EASE = [0.16, 1, 0.3, 1];
20
+ export function Reveal({ delay = 0, y = 28, className = "", children }) {
21
+ const reduce = useReducedMotion();
22
+ return (
23
+ <motion.div className={className} initial={reduce ? false : { opacity: 0, y }} whileInView={{ opacity: 1, y: 0 }}
24
+ viewport={{ once: true, margin: "0px 0px -12% 0px" }} transition={{ duration: 0.9, ease: EASE, delay }}>
25
+ {children}
26
+ </motion.div>
27
+ );
28
+ }
29
+ ```
30
+ Use on paragraphs, buttons, list rows (stagger rows by 0.05 to 0.08s).
31
+
32
+ ### 2. Headline lines: words rise out of a mask
33
+ ```jsx
34
+ export function Lines({ text, as: Tag = "h2", className = "", delay = 0, onMount = false }) {
35
+ const reduce = useReducedMotion();
36
+ const ref = useRef(null);
37
+ const inView = useInView(ref, { once: true, margin: "0px 0px -10% 0px" });
38
+ return (
39
+ <Tag ref={ref} className={className} aria-label={text}>
40
+ {text.split(" ").map((w, i) => (
41
+ <span key={i} aria-hidden="true" className="inline-block overflow-hidden pb-[0.08em] align-bottom">
42
+ <motion.span className="inline-block" initial={reduce ? false : { y: "110%" }}
43
+ animate={onMount || inView ? { y: "0%" } : undefined}
44
+ transition={{ duration: 1, ease: EASE, delay: delay + i * 0.06 }}>
45
+ {w}{" "}
46
+ </motion.span>
47
+ </span>
48
+ ))}
49
+ </Tag>
50
+ );
51
+ }
52
+ ```
53
+ Use on every H1 and H2. Hero headline: `onMount`. Long quotes: stagger 0.025.
54
+
55
+ ### 3. Image curtain: clip reveal plus settle
56
+ ```jsx
57
+ <motion.div className="overflow-hidden aspect-[4/5]"
58
+ initial={{ clipPath: "inset(100% 0% 0% 0%)" }} whileInView={{ clipPath: "inset(0% 0% 0% 0%)" }}
59
+ viewport={{ once: true }} transition={{ duration: 1.3, ease: EASE }}>
60
+ <motion.img src={src} alt={alt} className="h-full w-full object-cover"
61
+ initial={{ scale: 1.18 }} whileInView={{ scale: 1 }} viewport={{ once: true }} transition={{ duration: 1.8, ease: EASE }} />
62
+ </motion.div>
63
+ ```
64
+ Use on feature images and tiles. Not on every small thumbnail.
65
+
66
+ ### 4. Parallax image: slower than the page
67
+ ```jsx
68
+ const ref = useRef(null);
69
+ const { scrollYProgress } = useScroll({ target: ref, offset: ["start end", "end start"] });
70
+ const y = useTransform(scrollYProgress, [0, 1], ["-10%", "10%"]);
71
+ <div ref={ref} className="relative overflow-hidden h-[75svh]">
72
+ <motion.img src={src} alt="" style={{ y, scale: 1.25 }} className="absolute inset-0 h-full w-full object-cover" />
73
+ </div>
74
+ ```
75
+ Use on the hero image and full-bleed bands. Strength 6 to 12%.
76
+
77
+ ### 5. Hover details
78
+ - Links: an underline that draws from the left (`scale-x-0` to `scale-x-100`, origin-left, 500ms).
79
+ - Images in cards: `scale-[1.05]` over 1.2s.
80
+ - Buttons: lift 2px or invert colours over 300 to 500ms. Card actions slide up into view on hover (desktop), always visible on touch.
81
+
82
+ ### 6. Numbers that count up
83
+ Animate from 0 to the value once in view, 1.6s, tabular figures. Only real numbers.
84
+
85
+ ## Signature moments (pick ONE per site)
86
+
87
+ | Signature | Best for | How |
88
+ | --- | --- | --- |
89
+ | **Intro curtain** | Fashion, studios, restaurants | Full-screen panel in the ink colour, brand name rises in, panel lifts away after ~1s. Once per visit (sessionStorage). Delay the hero entrance until it lifts. |
90
+ | **Sticky lookbook** | Products, case studies | Image sticky on one side (`lg:sticky lg:top-24`), details scroll past on the other. On phones: image first, then details. |
91
+ | **Story by the hour / chapters** | Restaurants, brands with a process | Image sticky; chapters scroll; an IntersectionObserver (`rootMargin: "-45% 0px -45% 0px"`) sets the active chapter and the image crossfades. |
92
+ | **Pinned sideways gallery** | Photographers, travel, portfolios | Section `h-[300vh]`, inner `sticky top-0 h-svh`, row translates `x` from 0 to -60% with scroll progress. Phones: a swipe row with snap. |
93
+ | **Floating preview** | Index lists, menus | On hover (fine pointers), a small image follows the pointer with a soft spring (stiffness ~150). |
94
+ | **Custom cursor** | Creative portfolios | A 10px dot that grows to a labelled circle ("View") over work. Only on `(hover: hover) and (pointer: fine)`. Never hide the real cursor on forms. |
95
+ | **Marquee band** | Stores, events, studios | One line of short facts moving slowly (30 to 40s per loop), separated by small shapes, never by dashes. |
96
+ | **Breathing or live element** | Wellness, apps | A circle scaling 1 to 1.5 over 4s and back; or the real app running inside a phone frame. |
97
+
98
+ ## Pages: where motion goes
99
+ - **Hero**: headline lines on load, subline and buttons reveal 0.4 to 0.6s later, image settles or drifts (parallax).
100
+ - **Every section**: heading = Lines, body = Reveal, images = curtain or parallax, rows staggered.
101
+ - **Footer**: the oversized wordmark rises in once.
102
+ - **Mobile**: keep reveals and the signature (adapted), drop cursor effects and hover-only previews. Never pin long sections on phones.
103
+
104
+ ## Mobile apps
105
+ Screen pushes slide 100% (300 to 350ms), sheets spring up, list items fade in, buttons scale to 0.97 on press. The phone frame in a landing page can rise on load, float gently (y ±10px over 6s) and tilt toward the pointer on desktop.
@@ -0,0 +1,63 @@
1
+ # Page plans
2
+
3
+ Section order for each kind of site. Every page starts with a nav and ends with a closing call to action and a full footer. Blueprint names refer to `sections.md`. Pick the direction first (`directions.md`).
4
+
5
+ ## Fashion or clothing store (direction: Atelier or Champagne)
6
+ 1. Nav: logo center, Shop / Collections / Journal left, Search / Account / Bag right (N2)
7
+ 2. Hero: full-bleed campaign photo with one line of type and "Shop the collection" (H2)
8
+ 3. New arrivals: 4 to 8 products, image 4:5, name, price (S4 product grid)
9
+ 4. Story split: atelier photo + 3 short lines about how it is made (S2 split)
10
+ 5. Category tiles: 2 or 3 large image links ("Coats", "Knitwear", "Shirts") (S5)
11
+ 6. Quote: one customer line, large serif (S7)
12
+ 7. Service row: delivery, returns, repairs in plain words, hairline separated (S6 list)
13
+ 8. Newsletter: one field, one line of value ("Two emails a month, new pieces first") (S10)
14
+ 9. Footer with oversized wordmark (F2)
15
+ Product page: gallery left (sticky on desktop), details right: name, price, size selector, add to bag, fabric and care in an accordion, delivery note.
16
+
17
+ ## Portfolio: photographer, artist, designer (direction: Noir Gallery)
18
+ 1. Nav: name left, Work / About / Contact right (N1)
19
+ 2. Hero: type-only statement over a calm background, one line about the practice (H3)
20
+ 3. Selected work: index list or large alternating images (S8 index list)
21
+ 4. Full-bleed image band (S3)
22
+ 5. About: portrait or studio photo + short bio, clients as a plain list (S2)
23
+ 6. Services or approach: 3 to 4 items with hairlines, no icons (S6)
24
+ 7. Contact: big email link and availability line (S9 CTA)
25
+ 8. Footer: minimal (F1)
26
+
27
+ ## Restaurant, cafe, bar (direction: Harbour)
28
+ 1. Nav with "Book a table" button (N1)
29
+ 2. Hero: dish or room photo, name, one line, booking button (H1 split or H2)
30
+ 3. Opening hours and address, very visible (S6 list)
31
+ 4. Menu: printed card style, 2 columns, dish, short description, price (S11 menu)
32
+ 5. Story: the kitchen, the producers (S2)
33
+ 6. Gallery: 3 to 5 photos in an uneven grid (S5)
34
+ 7. Private dining or events (S2 reversed)
35
+ 8. Booking CTA with phone number (S9)
36
+ 9. Footer with map link and hours (F1)
37
+
38
+ ## Architecture, interiors, hotel (direction: Stone and Linen)
39
+ Nav (N1), hero full-bleed (H2), intro statement (S1), projects alternating splits (S2 x3), approach list (S6), press or awards as plain text list (S6), contact (S9), footer (F2).
40
+
41
+ ## SaaS, software, fintech (direction: Swiss Precision or Midnight Ink)
42
+ 1. Nav with "Sign in" link and "Start free" button (N1)
43
+ 2. Hero: headline, subline, two buttons, product screenshot in a thin frame (H1)
44
+ 3. Logos of real customers only if provided; otherwise skip (S12)
45
+ 4. Problem and outcome statement (S1)
46
+ 5. Three to five features as a hairline list with a screenshot per feature (S2 alternating)
47
+ 6. How it works in 3 steps (S6 numbered)
48
+ 7. Quote (S7)
49
+ 8. Pricing: 2 or 3 plans, flat, one highlighted with a border (S13)
50
+ 9. FAQ (S14)
51
+ 10. CTA (S9) and footer (F1)
52
+
53
+ ## AI product (direction: Midnight Ink or Swiss Precision)
54
+ Same as SaaS, plus a live demo block (input and a real example output) right after the hero, and a plain "What happens to your data" section before pricing.
55
+
56
+ ## Personal brand, coach, consultant (direction: Champagne or Stone and Linen)
57
+ Nav, hero with portrait (H1), what I help with (S6), method (S2), results with real names only if given (S7), offer and price (S13), FAQ (S14), booking CTA (S9), footer.
58
+
59
+ ## Event or conference (direction: Bold Editorial)
60
+ Nav, hero with date and city huge (H3), speakers grid (S4 adapted), schedule by day in tabs (S6), venue (S2), tickets (S13), FAQ (S14), CTA (S9), footer (F2).
61
+
62
+ ## Mobile app landing (direction to match the app)
63
+ Nav, hero with a phone showing the real home screen (H1), three screens with one line each (S2 alternating), privacy statement (S1), quote (S7), download buttons (S9), footer (F1). App screens themselves: see `mobile-app.md`.
@@ -0,0 +1,50 @@
1
+ # Responsive and mobile
2
+
3
+ ## Principles
4
+ - Mobile first: base styles for about 360px, then add space and columns as the screen widens.
5
+ - Size components by their container, not the screen, when they can sit in different places (cards in a sidebar, a widget in a grid). Use container queries (`@container` and `@sm:`/`@md:` in Tailwind v4) or a ResizeObserver.
6
+ - Fluid everything: `clamp()` for type, padding and gaps, so there are fewer breakpoints to manage.
7
+ - No horizontal scroll on the page, ever. Wide content (tables, code) scrolls inside its own box.
8
+
9
+ ## Breakpoints to test
10
+ 320 (small Android), 375 and 390 (iPhone), 430 (large phones), 768 (tablet portrait), 1024 (tablet landscape, small laptop), 1280, 1440, 1920.
11
+ Laptops at 125% or 150% Windows scaling report about 1100 to 1280px wide and 600 to 720px tall: check that the hero and nav still fit.
12
+
13
+ ## Layout patterns
14
+ - Grids: `grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr))` instead of fixed column counts.
15
+ - Navs: links collapse into a menu when the bar runs out of room; the menu panel animates with transform and opacity only.
16
+ - Heroes: stack text over media on phones; keep the CTA visible in the first screen (`min-height: 100svh` minus the nav).
17
+ - Use `100svh` / `100dvh` instead of `100vh` for full-height mobile sections (the browser bar changes height).
18
+ - Long text: `max-width: 65ch`; avoid text over busy backgrounds without a scrim.
19
+
20
+ ## Touch and input
21
+ - Targets at least 44 by 44px with 8px between them.
22
+ - Hover effects need a touch alternative (tap to reveal, always-visible actions).
23
+ - Use `pointer` media features: `@media (hover: hover) and (pointer: fine)` for hover-only flourishes like magnetic or cursor effects; switch them off on touch.
24
+ - Inputs: correct `type` and `inputmode` (`email`, `tel`, `numeric`), `autocomplete` values, font size 16px or more (iOS zooms below that).
25
+
26
+ ## Safe areas (notches, home indicator)
27
+ ```html
28
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
29
+ ```
30
+ ```css
31
+ .bottom-bar { padding-bottom: max(0.75rem, env(safe-area-inset-bottom)); }
32
+ .top-bar { padding-top: max(0.75rem, env(safe-area-inset-top)); }
33
+ ```
34
+
35
+ ## Installable web app (PWA)
36
+ - `manifest.webmanifest` with `name`, `short_name`, `start_url`, `display: "standalone"`, `theme_color`, `background_color`, icons 192 and 512 (plus a maskable icon).
37
+ - A service worker for offline caching (for Vite: `vite-plugin-pwa`). Cache static assets; never cache API responses that contain private data.
38
+ - `theme-color` meta for light and dark: `<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#09090b">`.
39
+ - Native app needed later? Wrap the same web app with Capacitor; keep the code web-standard so that stays easy.
40
+
41
+ ## Performance on phones
42
+ - Animate `transform` and `opacity` only; avoid big `backdrop-filter` blurs on elements that move or resize.
43
+ - WebGL and canvas: cap device pixel ratio at 1.5 to 2, pause when off screen (IntersectionObserver), stop when the tab is hidden.
44
+ - Lazy-load heavy sections (`React.lazy`, dynamic import) and images below the fold.
45
+ - Budget: LCP under 2.5s, CLS under 0.1, INP under 200ms on a mid-range phone.
46
+
47
+ ## Reduced motion and accessibility
48
+ - `@media (prefers-reduced-motion: reduce)`: stop parallax, auto-play, large movement; keep simple fades.
49
+ - Visible focus rings (`focus-visible:ring-2`), logical tab order, `aria-label` on icon-only buttons, `aria-expanded` on menus.
50
+ - Text contrast at least 4.5:1 (3:1 for large text and UI borders).
@@ -0,0 +1,266 @@
1
+ # Section blueprints
2
+
3
+ React + Tailwind CSS v4 with the tokens from `directions.md` (`bg-bg`, `text-ink`, `text-muted`, `border-line`, `bg-accent`, `font-display`). Replace copy and images, keep the structure, spacing and responsive classes. Plain HTML works the same way (`className` becomes `class`).
4
+
5
+ Shared wrappers used below:
6
+ ```jsx
7
+ const Container = ({ className = "", children }) => <div className={`mx-auto w-full max-w-[1360px] px-5 sm:px-8 lg:px-12 ${className}`}>{children}</div>;
8
+ const Section = ({ className = "", children, id }) => <section id={id} className={`py-20 sm:py-28 lg:py-36 ${className}`}>{children}</section>;
9
+ const Img = ({ src, alt, ratio = "aspect-[4/5]", className = "" }) => (
10
+ <div className={`overflow-hidden bg-surface ${ratio} ${className}`}>
11
+ <img src={src} alt={alt} loading="lazy" decoding="async" className="h-full w-full object-cover transition-transform duration-700 ease-out hover:scale-[1.03]" />
12
+ </div>
13
+ );
14
+ ```
15
+ Unsplash image by id: `https://images.unsplash.com/<photo-id>?w=1600&q=80&auto=format&fit=crop`.
16
+
17
+ ---
18
+
19
+ ## N1. Nav: logo left, links right, menu on phones
20
+ ```jsx
21
+ function Nav() {
22
+ const [open, setOpen] = useState(false);
23
+ const links = [["Work", "#work"], ["About", "#about"], ["Contact", "#contact"]];
24
+ return (
25
+ <header className="sticky top-0 z-50 border-b border-line bg-bg/95 backdrop-blur-sm">
26
+ <Container className="flex h-16 items-center justify-between sm:h-20">
27
+ <a href="/" className="font-display text-xl tracking-tight">Halden</a>
28
+ <nav className="hidden items-center gap-8 text-[15px] md:flex">
29
+ {links.map(([l, h]) => <a key={l} href={h} className="text-muted transition-colors hover:text-ink">{l}</a>)}
30
+ <a href="#contact" className="bg-accent px-5 py-2.5 text-sm text-on-accent">Get in touch</a>
31
+ </nav>
32
+ <button className="grid h-11 w-11 place-items-center md:hidden" aria-label={open ? "Close menu" : "Open menu"} aria-expanded={open} onClick={() => setOpen(!open)}>
33
+ <span className="relative block h-3 w-6">
34
+ <span className={`absolute left-0 h-px w-full bg-ink transition-transform ${open ? "top-1.5 rotate-45" : "top-0"}`} />
35
+ <span className={`absolute left-0 h-px w-full bg-ink transition-transform ${open ? "top-1.5 -rotate-45" : "top-3"}`} />
36
+ </span>
37
+ </button>
38
+ </Container>
39
+ {open && (
40
+ <nav className="border-t border-line bg-bg md:hidden">
41
+ <Container className="flex flex-col py-4">
42
+ {links.map(([l, h]) => <a key={l} href={h} onClick={() => setOpen(false)} className="border-b border-line py-4 font-display text-2xl">{l}</a>)}
43
+ </Container>
44
+ </nav>
45
+ )}
46
+ </header>
47
+ );
48
+ }
49
+ ```
50
+ **N2 (store)**: three columns on desktop: links left, logo centered (`justify-self-center`), icons right (Search, Account, Bag with count). On phones: menu button left, logo center, bag right.
51
+
52
+ ## H1. Hero split: text left, image right
53
+ ```jsx
54
+ <Section className="pt-10 sm:pt-16">
55
+ <Container className="grid items-end gap-10 lg:grid-cols-12">
56
+ <div className="lg:col-span-6">
57
+ <h1 className="font-display text-[44px] leading-[1.02] sm:text-[64px] lg:text-[88px]">Fish from the morning boat</h1>
58
+ <p className="mt-6 max-w-md text-lg text-muted">Cooked over charcoal in a small room by the harbour. Thirty seats, one menu that changes daily.</p>
59
+ <div className="mt-10 flex flex-wrap items-center gap-6">
60
+ <a href="#book" className="bg-accent px-7 py-4 text-sm text-on-accent">Book a table</a>
61
+ <a href="#menu" className="text-sm underline decoration-line underline-offset-8 hover:decoration-ink">See the menu</a>
62
+ </div>
63
+ </div>
64
+ <Img className="lg:col-span-6" ratio="aspect-[4/5] sm:aspect-[5/4] lg:aspect-[4/5]" src="..." alt="Grilled fish on a dark plate" />
65
+ </Container>
66
+ </Section>
67
+ ```
68
+
69
+ ## H2. Hero full-bleed photo
70
+ ```jsx
71
+ <section className="relative h-[88svh] min-h-[560px] overflow-hidden">
72
+ <img src="..." alt="" className="absolute inset-0 h-full w-full object-cover" />
73
+ <div className="absolute inset-0 bg-black/35" />
74
+ <Container className="relative flex h-full flex-col justify-end pb-14 text-white sm:pb-20">
75
+ <h1 className="max-w-3xl font-display text-[44px] leading-[1.02] sm:text-[72px] lg:text-[104px]">The winter edit</h1>
76
+ <div className="mt-8 flex items-center gap-6">
77
+ <a href="#shop" className="bg-white px-7 py-4 text-sm text-black">Shop the collection</a>
78
+ </div>
79
+ </Container>
80
+ </section>
81
+ ```
82
+
83
+ ## H3. Type-only statement
84
+ ```jsx
85
+ <Section className="pt-16 sm:pt-24">
86
+ <Container>
87
+ <h1 className="font-display text-[48px] leading-[0.98] tracking-[-0.03em] sm:text-[84px] lg:text-[128px]">Landscape and architecture photography.</h1>
88
+ <div className="mt-12 grid gap-6 border-t border-line pt-6 text-muted sm:grid-cols-3">
89
+ <p>Based in Oslo</p><p>Working worldwide</p><p className="sm:text-right">Available from March</p>
90
+ </div>
91
+ </Container>
92
+ </Section>
93
+ ```
94
+
95
+ ## S1. Intro statement
96
+ ```jsx
97
+ <Section><Container className="grid gap-8 lg:grid-cols-12">
98
+ <p className="text-sm text-muted lg:col-span-3">About the studio</p>
99
+ <p className="font-display text-[28px] leading-[1.25] sm:text-[40px] lg:col-span-9">We design small buildings with long lives: houses, studios and a few public rooms, mostly in timber and stone.</p>
100
+ </Container></Section>
101
+ ```
102
+
103
+ ## S2. Split (image + text), alternate sides with `lg:order-2`
104
+ ```jsx
105
+ <Section><Container className="grid items-center gap-10 lg:grid-cols-12 lg:gap-16">
106
+ <Img className="lg:col-span-7" ratio="aspect-[3/2]" src="..." alt="Tailor cutting wool" />
107
+ <div className="lg:col-span-5">
108
+ <h2 className="font-display text-[36px] leading-[1.05] sm:text-[52px]">Cut in Porto</h2>
109
+ <p className="mt-6 text-lg text-muted">Every coat is cut and sewn in a workshop of eleven people. We make small runs, so nothing sits in a warehouse.</p>
110
+ <a href="#story" className="mt-8 inline-block text-sm underline underline-offset-8">Read the story</a>
111
+ </div>
112
+ </Container></Section>
113
+ ```
114
+
115
+ ## S3. Full-bleed image band
116
+ ```jsx
117
+ <section className="relative h-[70svh] min-h-[420px]"><img src="..." alt="Fjord at dawn" className="h-full w-full object-cover" /></section>
118
+ ```
119
+
120
+ ## S4. Product grid (store)
121
+ ```jsx
122
+ <Section><Container>
123
+ <div className="flex items-end justify-between gap-6">
124
+ <h2 className="font-display text-[36px] sm:text-[52px]">New in</h2>
125
+ <a href="#all" className="text-sm underline underline-offset-8">View all</a>
126
+ </div>
127
+ <ul className="mt-10 grid grid-cols-2 gap-x-4 gap-y-10 sm:gap-x-6 lg:grid-cols-4">
128
+ {products.map((p) => (
129
+ <li key={p.name}><a href={p.href} className="group block">
130
+ <Img src={p.img} alt={p.name} />
131
+ <div className="mt-4 flex items-baseline justify-between gap-3 text-[15px]">
132
+ <span>{p.name}</span><span className="tabular-nums text-muted">€{p.price}</span>
133
+ </div>
134
+ <p className="mt-1 text-sm text-muted">{p.note}</p>
135
+ </a></li>
136
+ ))}
137
+ </ul>
138
+ </Container></Section>
139
+ ```
140
+
141
+ ## S5. Large image tiles (categories or gallery)
142
+ ```jsx
143
+ <Section><Container className="grid gap-4 sm:gap-6 md:grid-cols-3">
144
+ {tiles.map((t, i) => (
145
+ <a key={t.label} href={t.href} className={`group relative block ${i === 0 ? "md:col-span-2" : ""}`}>
146
+ <Img src={t.img} alt={t.label} ratio={i === 0 ? "aspect-[4/3] md:aspect-auto md:h-full" : "aspect-[4/5]"} />
147
+ <span className="absolute bottom-5 left-5 bg-bg px-4 py-2 text-sm">{t.label}</span>
148
+ </a>
149
+ ))}
150
+ </Container></Section>
151
+ ```
152
+
153
+ ## S6. Hairline list (services, facts, steps, hours)
154
+ ```jsx
155
+ <Section><Container className="grid gap-10 lg:grid-cols-12">
156
+ <h2 className="font-display text-[36px] sm:text-[52px] lg:col-span-4">How we work</h2>
157
+ <ol className="lg:col-span-8">
158
+ {items.map((it, i) => (
159
+ <li key={it.title} className="grid gap-2 border-t border-line py-7 sm:grid-cols-[4rem_1fr_2fr] sm:gap-6">
160
+ <span className="tabular-nums text-muted">{String(i + 1).padStart(2, "0")}</span>
161
+ <h3 className="text-lg">{it.title}</h3>
162
+ <p className="text-muted">{it.body}</p>
163
+ </li>
164
+ ))}
165
+ </ol>
166
+ </Container></Section>
167
+ ```
168
+ Use numbers only when the order is real (steps). For facts and services, drop the number column.
169
+
170
+ ## S7. One large quote
171
+ ```jsx
172
+ <Section className="bg-surface"><Container className="max-w-4xl">
173
+ <blockquote className="font-display text-[30px] leading-[1.25] sm:text-[44px]">"I have had this coat for six winters. The lining was repaired for free last year."</blockquote>
174
+ <p className="mt-8 text-muted">Ana R., Lisbon</p>
175
+ </Container></Section>
176
+ ```
177
+
178
+ ## S8. Index list (portfolio)
179
+ ```jsx
180
+ <Section id="work"><Container>
181
+ {projects.map((p) => (
182
+ <a key={p.title} href={p.href} className="group grid grid-cols-[1fr_auto] items-baseline gap-4 border-t border-line py-6 sm:py-8">
183
+ <span className="font-display text-[32px] leading-none transition-colors group-hover:text-muted sm:text-[56px]">{p.title}</span>
184
+ <span className="text-sm text-muted">{p.place}, {p.year}</span>
185
+ <Img className="col-span-2 mt-4 sm:hidden" ratio="aspect-[3/2]" src={p.img} alt={p.title} />
186
+ </a>
187
+ ))}
188
+ </Container></Section>
189
+ ```
190
+
191
+ ## S9. Closing call to action
192
+ ```jsx
193
+ <Section className="bg-ink text-bg"><Container className="grid gap-10 lg:grid-cols-12 lg:items-end">
194
+ <h2 className="font-display text-[44px] leading-[1.02] sm:text-[72px] lg:col-span-8">Tables open thirty days ahead.</h2>
195
+ <div className="lg:col-span-4 lg:text-right">
196
+ <a href="#book" className="inline-block bg-bg px-7 py-4 text-sm text-ink">Book a table</a>
197
+ <p className="mt-4 text-sm opacity-70">Or call +351 21 000 0000</p>
198
+ </div>
199
+ </Container></Section>
200
+ ```
201
+
202
+ ## S10. Newsletter
203
+ ```jsx
204
+ <form className="flex max-w-md border-b border-ink" onSubmit={(e) => e.preventDefault()}>
205
+ <label htmlFor="email" className="sr-only">Email</label>
206
+ <input id="email" type="email" required autoComplete="email" placeholder="you@example.com" className="min-w-0 flex-1 bg-transparent py-4 outline-none placeholder:text-muted" />
207
+ <button className="py-4 pl-4 text-sm">Subscribe</button>
208
+ </form>
209
+ ```
210
+
211
+ ## S11. Menu card (restaurant)
212
+ ```jsx
213
+ <ul className="grid gap-x-16 md:grid-cols-2">
214
+ {dishes.map((d) => (
215
+ <li key={d.name} className="border-t border-line py-5">
216
+ <div className="flex items-baseline gap-3">
217
+ <span className="text-lg">{d.name}</span>
218
+ <span className="flex-1 translate-y-[-4px] border-b border-dotted border-line" />
219
+ <span className="tabular-nums">{d.price}</span>
220
+ </div>
221
+ <p className="mt-1 text-sm text-muted">{d.note}</p>
222
+ </li>
223
+ ))}
224
+ </ul>
225
+ ```
226
+
227
+ ## S12. Logos or press (only real ones)
228
+ A single row of 4 to 6 names set in the text font at 15px, muted, separated by wide gaps. No fake logos.
229
+
230
+ ## S13. Pricing (2 or 3 plans)
231
+ Plans side by side on desktop, stacked on phones. Each: name, price in large tabular figures, one line, 4 to 6 included items with hairlines, one button. Highlight one plan with a 1px ink border, not a gradient or a badge storm.
232
+
233
+ ## S14. FAQ
234
+ ```jsx
235
+ <div className="divide-y divide-line border-y border-line">
236
+ {faqs.map((f) => (
237
+ <details key={f.q} className="group py-6">
238
+ <summary className="flex cursor-pointer list-none items-center justify-between gap-6 text-lg">
239
+ {f.q}<span className="text-2xl leading-none transition-transform group-open:rotate-45">+</span>
240
+ </summary>
241
+ <p className="mt-4 max-w-2xl text-muted">{f.a}</p>
242
+ </details>
243
+ ))}
244
+ </div>
245
+ ```
246
+
247
+ ## F1. Footer: columns
248
+ ```jsx
249
+ <footer className="border-t border-line pt-16 pb-10"><Container>
250
+ <div className="grid gap-10 sm:grid-cols-2 lg:grid-cols-12">
251
+ <div className="lg:col-span-5"><p className="font-display text-2xl">Halden</p><p className="mt-3 max-w-xs text-muted">Landscape and architecture photography from Oslo.</p></div>
252
+ {columns.map((c) => (
253
+ <div key={c.title} className="lg:col-span-2">
254
+ <p className="text-sm text-muted">{c.title}</p>
255
+ <ul className="mt-4 space-y-3">{c.links.map((l) => <li key={l}><a href="#" className="hover:underline underline-offset-4">{l}</a></li>)}</ul>
256
+ </div>
257
+ ))}
258
+ </div>
259
+ <div className="mt-16 flex flex-col justify-between gap-4 border-t border-line pt-6 text-sm text-muted sm:flex-row">
260
+ <p>© 2026 Halden Studio</p><p>Privacy · Terms</p>
261
+ </div>
262
+ </Container></footer>
263
+ ```
264
+
265
+ ## F2. Footer with oversized wordmark
266
+ F1 plus, at the very bottom: `<p className="mt-12 font-display text-[22vw] leading-[0.8] tracking-[-0.04em]" aria-hidden="true">Ivoire</p>` with `overflow-hidden` on the footer.
@@ -0,0 +1,81 @@
1
+ # Security (front end)
2
+
3
+ No app is "100% secure", but these rules close the holes that real front ends ship with. Treat every item as required unless it clearly does not apply, and say so when you skip one.
4
+
5
+ ## 1. Secrets
6
+ - Anything in the browser bundle is public. That includes every `VITE_*`, `NEXT_PUBLIC_*` and `REACT_APP_*` variable.
7
+ - Never put API keys with write or billing access, database URLs, JWT secrets, SMTP passwords or service-account files in front-end code. Call your own server (or a serverless function) and keep the secret there.
8
+ - Publishable keys that are designed to be public (Stripe publishable key, Firebase web config, Maps keys) are fine, but restrict them by domain in the provider's dashboard.
9
+ - Add `.env*` to `.gitignore` (keep a committed `.env.example` with empty values). Never print secrets in logs, errors or chat replies.
10
+
11
+ ## 2. Cross-site scripting (XSS)
12
+ - React escapes text by default. Do not undo it: avoid `dangerouslySetInnerHTML`. If you must render HTML (CMS content, markdown), sanitise it first with DOMPurify.
13
+ - Never build `href`/`src` from user input without checking the protocol. Allow only `https:`, `http:`, `mailto:`, `tel:` and relative paths; block `javascript:` and `data:`.
14
+ - Never `eval`, `new Function` or `setTimeout("string")`.
15
+
16
+ ```js
17
+ export function safeUrl(url) {
18
+ try {
19
+ const u = new URL(url, window.location.origin);
20
+ return ["http:", "https:", "mailto:", "tel:"].includes(u.protocol) ? u.href : "#";
21
+ } catch {
22
+ return "#";
23
+ }
24
+ }
25
+ ```
26
+
27
+ ## 3. Links and embeds
28
+ - `target="_blank"` always with `rel="noopener noreferrer"`.
29
+ - Third-party iframes: `sandbox` with the fewest permissions, `loading="lazy"`, `referrerpolicy="strict-origin-when-cross-origin"`.
30
+ - Load third-party scripts only from trusted CDNs, pinned to an exact version, with Subresource Integrity (`integrity` + `crossorigin`) when the CDN supports it.
31
+
32
+ ## 4. Auth on the client
33
+ - Keep session tokens in `httpOnly`, `Secure`, `SameSite=Lax` (or `Strict`) cookies set by the server. Do not store JWTs or refresh tokens in `localStorage` or `sessionStorage` (any XSS can read them).
34
+ - Hiding a button is not access control. The server must check permission on every request.
35
+ - Log out clears the server session; on 401 responses redirect to sign in.
36
+
37
+ ## 5. Forms
38
+ - Validate on the client for UX (required, format, length) and again on the server for safety. Share the schema (for example Zod) when you can.
39
+ - Limit lengths (`maxLength`), use correct `type`/`autocomplete`, never echo raw errors from the server that reveal internals.
40
+ - Bot protection on public forms (sign up, contact): a honeypot field plus server-side rate limiting, or a CAPTCHA provider.
41
+ - File inputs: restrict `accept`, check size before upload, and let the server re-check type and size.
42
+
43
+ ## 6. Security headers (set by the host)
44
+ Example for Vercel (`vercel.json`); adapt the CSP to the domains you actually use, then test that nothing breaks.
45
+
46
+ ```json
47
+ {
48
+ "headers": [
49
+ {
50
+ "source": "/(.*)",
51
+ "headers": [
52
+ { "key": "Content-Security-Policy", "value": "default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; script-src 'self'; connect-src 'self' https://api.yourdomain.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'" },
53
+ { "key": "Strict-Transport-Security", "value": "max-age=63072000; includeSubDomains; preload" },
54
+ { "key": "X-Content-Type-Options", "value": "nosniff" },
55
+ { "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
56
+ { "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=()" },
57
+ { "key": "X-Frame-Options", "value": "DENY" }
58
+ ]
59
+ }
60
+ ]
61
+ }
62
+ ```
63
+ Netlify uses `_headers`; Nginx uses `add_header`. WebGL components need `script-src 'self'` only; Google Fonts needs the two font domains above.
64
+
65
+ ## 7. Dependencies and build
66
+ - Install packages only from npm with exact names (watch for typosquats). Prefer well-maintained packages.
67
+ - Run `npm audit` and fix high and critical issues; turn on Dependabot or Renovate.
68
+ - Commit the lockfile. Do not ship source maps with secrets in them; production builds should not expose `.env` or internal URLs.
69
+
70
+ ## 8. Privacy
71
+ - Ask before using camera, microphone, location or notifications, and only when the user acts.
72
+ - Cookie and analytics consent where the law requires it (EU, UK, India DPDP). Load analytics after consent.
73
+ - Do not log personal data to the console or third-party error trackers; scrub emails and tokens.
74
+
75
+ ## 9. Before you ship
76
+ - [ ] No secrets in the bundle (search the build output for `sk_`, `secret`, `password`, `mongodb+srv`, `BEGIN PRIVATE KEY`).
77
+ - [ ] No `dangerouslySetInnerHTML` with unsanitised input; all user URLs pass `safeUrl`.
78
+ - [ ] Every `target="_blank"` has `rel="noopener noreferrer"`.
79
+ - [ ] Tokens live in httpOnly cookies, not web storage.
80
+ - [ ] Security headers set and checked (for example with securityheaders.com).
81
+ - [ ] `npm audit` clean of high and critical issues.
@@ -0,0 +1,53 @@
1
+ # Taste: what premium means
2
+
3
+ Premium is not more effects. It is fewer, better decisions made on purpose. Think of a good hotel, a fashion house, a watch brand: quiet surfaces, beautiful type, great photos, nothing extra.
4
+
5
+ ## Do
6
+ - **Let type carry the design.** Large display headlines (56 to 120px on desktop, 40 to 56px on phones), tight leading (1.0 to 1.1), slightly negative tracking. Body text calm and readable.
7
+ - **Use a grid.** 12 columns on desktop, 4 on phones, generous gutters (24 to 32px). Align edges. Asymmetry is good when it is deliberate (text in columns 1 to 5, image in 7 to 12).
8
+ - **Hairlines over boxes.** Separate content with 1px rules in the line colour instead of cards with shadows. When you need cards, keep them flat: a border or a background shift, no heavy shadow.
9
+ - **Photography first.** Big, well-cropped images with one consistent mood. Crop with intent (`object-cover`, a set aspect ratio like 4:5 for products, 3:2 for scenes).
10
+ - **Small details that feel expensive.** Real numbers ("Made in Porto, 312 pieces"), a subtle grain-free surface, consistent 1px borders, perfect alignment, considered empty space.
11
+ - **One signature move.** Each site gets one memorable idea: an oversized wordmark in the footer, a full-bleed photo that scrolls slower, a large serif number list, a sticky product image. Only one.
12
+ - **Buttons that look tailored.** Solid fill in ink or accent, generous padding (14px by 28px), either square (radius 0 to 4px) or a full pill. Secondary buttons are text links with an underline that grows on hover.
13
+ - **Icons sparingly.** Thin line icons (1.5px stroke) only where they help scanning. Never an icon on every heading.
14
+
15
+ ## Do not (the generic AI look)
16
+ - Gradients of any kind, glowing blobs, aurora backgrounds, gradient text.
17
+ - Purple, blue and pink "tech" palettes by default; neon accents on near-black.
18
+ - Glassmorphism everywhere, frosted cards on busy backgrounds.
19
+ - Three identical rounded cards with an icon, a title and two lines, repeated down the page.
20
+ - Everything centered. Use left alignment for most text; center only short statements.
21
+ - Tiny all-caps labels above every heading, middle dots between every word, arrows after every link.
22
+ - Big fake stats ("10x faster", "99.9% uptime") without context; fake logos; testimonials from "Sarah J., CEO".
23
+ - Default fonts used without thought (Inter, Roboto, Arial, Space Grotesk, Poppins) as the display face.
24
+ - Emoji, sparkles, rocket icons, "✨ New".
25
+ - Drop shadows on everything, radius 16px on everything, hover lift on every card.
26
+ - Stock photos of handshakes, people pointing at laptops, or 3D blobs.
27
+
28
+ ## Colour rules
29
+ - Background is never pure white or pure black: use a warm or cool off-white (#F4F1EA, #FAFAF8) or a soft black (#0E0E0E, #111214).
30
+ - Text colour has enough contrast (4.5:1) and is a deep neutral, not pure black on white.
31
+ - Accent appears on: the primary button, active states, one or two small marks. That is all.
32
+ - Dark sections inside a light site (and the opposite) are a strong, premium rhythm: one per page is enough.
33
+
34
+ ## Type rules
35
+ - Display: a characterful serif (Cormorant Garamond, Fraunces, Instrument Serif, Bodoni Moda, DM Serif Display, Newsreader, EB Garamond) or a refined grotesk (Schibsted Grotesk, Hanken Grotesk, Familjen Grotesk, Archivo, Syne for bold brands).
36
+ - Text: a quiet sans (Manrope, Figtree, Hanken Grotesk, Onest, Jost, Instrument Sans) or the same family as display.
37
+ - Scale: 14 / 16 / 18 / 22 / 28 / 40 / 56 / 80 / 112px. Pick from it, nothing in between.
38
+ - Weights: display 400 to 500 for serifs, 500 to 700 for grotesks. Body 400. Never more than three weights on a page.
39
+ - Numbers: use tabular figures for prices and stats (`font-variant-numeric: tabular-nums`).
40
+
41
+ ## Layout rhythm
42
+ - Section padding: `py-24 md:py-32 lg:py-40`.
43
+ - Page gutter: `px-5 sm:px-8 lg:px-12`, max width 1280 to 1440px.
44
+ - Vertical spacing between heading and body: 16 to 24px; between body and button: 32px.
45
+ - Alternate section layouts (split, full-bleed, list, grid) so the page has rhythm; never repeat the same layout three times in a row.
46
+
47
+ ## Motion rules
48
+ Premium sites move. A static page looks like a template. Follow `motion.md`:
49
+ - On load: the headline rises word by word out of a mask, then the subline and buttons, then the hero image settles from a slow zoom.
50
+ - On scroll: headings rise in, images uncover like a curtain or drift slower than the page, list rows arrive one after another, numbers count up.
51
+ - One signature moment per site (intro curtain, sticky lookbook, pinned gallery, floating preview...).
52
+ - Hover: underlines draw in, images scale 1.05 slowly, buttons invert or lift 2px. No bouncing, no spinning.
53
+ - Reduced motion: everything visible at rest.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apex-ui-kit",
3
- "version": "2.12.2",
3
+ "version": "2.14.0",
4
4
  "license": "MIT",
5
5
  "description": "116 free, animated UI components for React, styled with Tailwind CSS. Add any of them with one CLI command.",
6
6
  "homepage": "https://apex-ui.in",
@@ -26,7 +26,10 @@
26
26
  "webgl",
27
27
  "ai ui",
28
28
  "apexui",
29
- "apex ui kit"
29
+ "apex ui kit",
30
+ "ai agent skill",
31
+ "claude skill",
32
+ "cursor rules"
30
33
  ],
31
34
  "type": "module",
32
35
  "main": "dist/cli/index.js",
@@ -37,7 +40,7 @@
37
40
  "dist"
38
41
  ],
39
42
  "scripts": {
40
- "build": "tsc && copyfiles -u 2 src/ApexUI-Kit/**/* dist/ApexUI-Kit"
43
+ "build": "tsc && copyfiles -u 2 src/ApexUI-Kit/**/* dist/ApexUI-Kit && copyfiles -u 1 src/skill/**/* dist"
41
44
  },
42
45
  "dependencies": {
43
46
  "@fortawesome/fontawesome-svg-core": "^7.0.0",