@xenterprises/nuxt-x-marketing
v1.15.0
Published
A comprehensive Nuxt layer for building marketing websites with 38+ pre-built components. Features dark mode support, responsive design, accessibility, and seamless integration with Nuxt UI.
Readme
@xenterprises/nuxt-x-marketing
A comprehensive Nuxt layer for building marketing websites with 38+ pre-built components. Features dark mode support, responsive design, accessibility, and seamless integration with Nuxt UI.
Features
- 38+ Marketing Components - Hero, Features, Pricing, Testimonials, Blog, Affiliate/Review, and more
- Consent-aware tracking - Drop GTM/GA4/Clarity IDs in
app.config, the cookie banner auto-fires them on accept - Zero Required Props - Every component works out of the box with sensible defaults
- Dark Mode Support - All components support light and dark themes
- Responsive Design - Mobile-first approach with tablet and desktop breakpoints
- Accessible - WCAG 2.0 AA compliant with proper ARIA attributes
- Customizable - Props-based configuration with slot overrides
- Nuxt UI v4 Integration - Built on top of Nuxt UI v4 components
- Animations - Scroll-triggered fade-in animations and parallax effects
- Two Footer Modes - Generic white-label (
XMarkLayoutFooter) and X Enterprises branded (XFooter)
Installation
npm install @xenterprises/nuxt-x-marketingAdd the layer to your nuxt.config.js:
export default defineNuxtConfig({
extends: "@xenterprises/nuxt-x-marketing",
});Quick Start
What the consumer writes
The layer is batteries-included: it ships a default app shell (navbar + footer + cookie-consent banner) and default pages (/, /blog, /blog/[...slug]). You do not write an app.vue — configure the shell in app/app.config.ts under the xMarketing namespace (it must live in app/, not the project root):
// app/app.config.ts
export default defineAppConfig({
xMarketing: {
name: "Acme Inc",
url: "https://acme.com",
header: {
logo: { src: "/logo.svg", srcDark: "/logo-dark.svg", alt: "Acme" },
nav: {
links: [
{ label: "Pricing", to: "/#pricing" },
{ label: "Blog", to: "/blog" },
],
buttons: [{ label: "Get Started", to: "/signup", color: "primary" }],
},
},
footer: {
logo: { src: "/logo.svg", alt: "Acme" },
body: "Building the future of modern software.",
socials: [
{ name: "GitHub", url: "https://github.com/acme", icon: "i-lucide-github" },
],
columns: [
{
headerLabel: "Product",
links: [{ label: "Blog", to: "/blog" }],
},
],
},
blog: { active: true, title: "Blog" },
tracking: { gtmId: "GTM-XXXXXXX" }, // optional; fires only after consent
},
});Blog posts are markdown in content/blog/*.md — re-declare the blog collection in your own content.config.ts so Nuxt Content binds it to your content dir (the layer's .playground/content.config.ts is the reference).
Opting out of the defaults
Every shipped default has a switch or a standard Nuxt override:
xMarketing.header.active: false— removes the default navbar from the shellxMarketing.footer.active: false— removes the default footerxMarketing.consent.active: false— removes the cookie-consent banner (tracking scripts then never fire)xMarketing.blog.active: false— the shipped/blogpages 404- Replace the whole shell by shipping your own
app/app.vue; replace any page by shipping the same path underapp/pages/(standard Nuxt layer overriding)
Example Landing Page
All components work with zero required props — just drop them in and customize as needed:
<template>
<div>
<!-- Hero — works with zero props, or fully customized -->
<XMarkHero
:img="{ src: 'https://images.unsplash.com/photo-1551434678-e076c223a692', alt: 'Hero' }"
eyebrow="Welcome to the future"
title="Build something amazing today"
subtitle="The modern platform for teams who want to ship faster."
align="left"
overlay="gradient"
:buttons="[
{ label: 'Get Started Free', color: 'primary' },
{ label: 'Watch Demo', variant: 'outline' }
]"
/>
<!-- Features — works with zero props, or pass your own -->
<XMarkSection id="features" bg="default" padding="xl">
<header class="text-center max-w-3xl mx-auto mb-16">
<h2 class="xText-headline">Everything you need to succeed</h2>
</header>
<XMarkFeatures :features="features" layout="grid" :columns="3" />
</XMarkSection>
<!-- Testimonials -->
<XMarkSection bg="subtle" padding="xl">
<XMarkTestimonials :testimonials="testimonials" layout="grid" />
</XMarkSection>
<!-- Pricing -->
<XMarkSection bg="default" padding="xl">
<XMarkPricingPlans :plans="pricingPlans" />
</XMarkSection>
</div>
</template>
<script setup>
const features = [
{ icon: 'i-lucide-zap', title: 'Lightning Fast', description: 'Built for speed.' },
{ icon: 'i-lucide-shield-check', title: 'Secure by Default', description: 'Enterprise-grade security.' },
{ icon: 'i-lucide-users', title: 'Team Collaboration', description: 'Real-time collaboration tools.' },
]
const testimonials = [
{ quote: 'This platform transformed how our team works.', name: 'Sarah Chen', title: 'CTO', company: 'TechStart', rating: 5 },
]
const pricingPlans = [
{ name: 'Starter', price: '$19', period: '/month', features: ['5 team members', 'Basic analytics'], button: { label: 'Start Free Trial' } },
{ name: 'Pro', price: '$49', period: '/month', features: ['25 team members', 'Advanced analytics'], button: { label: 'Start Free Trial' }, popular: true },
]
</script>Components
Core Layout
XMarkLayoutNavbar
Fixed navigation header with transparent-to-solid scroll transition. Falls back to xMarketing.header (logo, nav links, nav buttons) when props are omitted.
<XMarkLayoutNavbar
:links="[{ label: 'Features', to: '/#features' }]"
:logo="{ src: '/logo.svg', srcDark: '/logo-dark.svg', alt: 'Company' }"
:buttons="[
{ label: 'Sign In', to: '/login', variant: 'ghost' },
{ label: 'Get Started', to: '/signup', color: 'primary' },
]"
:transparent="true"
:scroll-threshold="100"
/>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| links | Array | xMarketing.header.nav.links | Navigation links { label, to } |
| logo | Object | xMarketing.header.logo | Logo { src, srcDark?, alt? } |
| buttons | Array | xMarketing.header.nav.buttons | Action buttons { label, to, color?, variant?, icon? } |
| transparent | Boolean | true | Start transparent over hero |
| scrollThreshold | Number | 100 | Pixels before transition |
XMarkSection
Section wrapper with background variants and optional patterns.
<XMarkSection bg="subtle" padding="xl" pattern="dots">
<h2>Section Content</h2>
</XMarkSection>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| bg | String | 'default' | default, subtle, elevated, bold, transparent |
| padding | String | 'lg' | none, sm, md, lg, xl |
| container | String | 'lg' | sm, md, lg, xl, full, none |
| pattern | String | '' | dots, grid, diagonal, topography, circuit, waves |
| patternOpacity | Number | 0.05 | Pattern opacity (0-1) |
| bgImage | String | '' | Background image URL |
| parallax | Boolean | false | Enable parallax on bg image |
XMarkLayoutFooter
Full footer with brand, link columns, social icons, and newsletter.
<XMarkLayoutFooter
:logo="{ src: '/logo.svg', alt: 'Company' }"
description="Building the future of modern software."
:social="[{ name: 'Twitter', href: 'https://twitter.com', icon: 'i-lucide-twitter' }]"
:columns="[{ title: 'Product', links: [{ label: 'Features', to: '/features' }] }]"
:legal-links="[{ label: 'Privacy', to: '/privacy' }]"
:has-newsletter="true"
@newsletter-submit="handleSubmit"
/>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| logo | Object | null | Logo { src, alt? } |
| description | String | '' | Brand description |
| social | Array | [] | Social links { name, href, icon } |
| columns | Array | [] | Link columns { title, links: [{ label, to }] } |
| legalLinks | Array | [] | Legal links { label, to } |
| copyright | String | auto | Copyright text |
| hasNewsletter | Boolean | false | Show newsletter section |
XMarkLayoutFooterLegal
Minimal footer with copyright and legal links only.
<XMarkLayoutFooterLegal
copyright="© 2026 Acme Inc. All rights reserved."
:links="[{ label: 'Privacy', to: '/privacy' }]"
/>Hero & Landing
XMarkHero
Full-screen hero with image/video background and overlay.
<XMarkHero
image-src="https://example.com/hero.jpg"
video-src="https://example.com/hero.mp4"
eyebrow="Welcome"
title="Build something amazing"
subtitle="The modern platform for teams."
align="left"
vertical-align="center"
overlay="gradient"
:show-scroll-indicator="true"
>
<template #actions>
<UButton size="xl" color="white">Get Started</UButton>
</template>
</XMarkHero>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| imageSrc | String | '' | Background image URL |
| videoSrc | String | '' | Background video URL |
| eyebrow | String | '' | Small text above title |
| title | String | required | Main headline |
| subtitle | String | '' | Supporting text |
| align | String | 'left' | left, center, right |
| verticalAlign | String | 'center' | center, bottom |
| overlay | String | 'gradient' | light, heavy, gradient, none |
| showScrollIndicator | Boolean | true | Show scroll arrow |
Content Sections
XMarkFeatures
Feature grid with icons, images, and optional links.
<XMarkFeatures
:features="[
{ icon: 'i-lucide-zap', title: 'Fast', description: 'Lightning fast performance.' },
{ icon: 'i-lucide-shield', title: 'Secure', description: 'Enterprise security.' },
]"
layout="grid"
:columns="3"
/>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| features | Array | required | Features { icon?, image?, title, description, link? } |
| layout | String | 'grid' | grid, list, alternating |
| columns | Number | 3 | Grid columns (2, 3, or 4) |
XMarkTestimonials
Testimonial cards in grid or carousel layout.
<XMarkTestimonials
:testimonials="[
{
quote: 'Amazing product!',
name: 'John Doe',
title: 'CEO',
company: 'Acme Inc',
avatar: 'https://example.com/avatar.jpg',
},
]"
layout="grid"
/>XMarkPricing
Pricing cards with billing toggle and popular badge.
<XMarkPricing
:plans="[
{
name: 'Starter',
price: '$19',
period: 'month',
description: 'Perfect for small teams.',
features: ['5 team members', 'Basic analytics'],
cta: 'Start Free Trial',
},
{
name: 'Pro',
price: '$49',
period: 'month',
features: ['25 team members', 'Advanced analytics'],
cta: 'Start Free Trial',
popular: true,
},
]"
:show-billing-toggle="true"
:yearly-discount="20"
@select="handlePlanSelect"
/>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| plans | Array | required | Pricing plans |
| highlighted | String | '' | Plan name to highlight |
| showBillingToggle | Boolean | false | Show monthly/yearly toggle |
| yearlyDiscount | Number | 20 | Yearly discount percentage |
XMarkComparison
Feature comparison table across plans.
<XMarkComparison
:plans="['Starter', 'Pro', 'Enterprise']"
:highlighted="'Pro'"
:feature-groups="[
{
name: 'Core Features',
features: [
{ name: 'Users', values: ['5', '25', 'Unlimited'] },
{ name: 'Storage', values: ['1GB', '10GB', 'Unlimited'] },
],
},
]"
/>XMarkNewsletter
Email signup form with variants.
<XMarkNewsletter
title="Subscribe to our newsletter"
description="Get the latest updates."
:button="{ label: 'Subscribe' }"
placeholder="Enter your email"
variant="default"
@submit="handleSubmit"
/>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| title | String | 'Subscribe...' | Form title |
| description | String | '' | Form description |
| button | Object | { label: 'Subscribe' } | Button config { label, color? } |
| variant | String | 'default' | default, minimal, inline |
Blog Components
XMarkBlogCard
Blog post card with image, title, excerpt, and author.
<XMarkBlogCard
:post="{
title: 'Getting Started with Nuxt',
slug: 'getting-started',
excerpt: 'Learn how to build modern web apps.',
image: '/blog/cover.jpg',
date: '2024-01-15',
author: { name: 'John Doe', avatar: '/avatars/john.jpg' },
category: 'Tutorial',
}"
variant="default"
/>XMarkBlogList
Blog listing grid with optional featured post.
<XMarkBlogList
:posts="posts"
:columns="3"
:show-featured="true"
/>XMarkBlogDetail
Full blog post view with author, share buttons, and navigation.
<XMarkBlogDetail
:post="post"
:author="author"
:related-posts="relatedPosts"
/>XMarkBlogSidebar
Blog sidebar with search, categories, tags, and recent posts.
<XMarkBlogSidebar
:categories="['Tutorials', 'News', 'Updates']"
:tags="['vue', 'nuxt', 'javascript']"
:recent-posts="recentPosts"
:show-search="true"
@search="handleSearch"
@category-click="handleCategoryClick"
/>XMarkBlogAuthor
Author bio with social links.
<XMarkBlogAuthor
:author="{
name: 'John Doe',
title: 'Senior Developer',
avatar: '/avatars/john.jpg',
bio: 'Passionate about building great software.',
social: { twitter: 'https://twitter.com/johndoe' },
}"
variant="full"
/>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| author | Object | required | Author data |
| variant | String | 'inline' | inline, sm, full, card |
XMarkBlogNavigation
Previous/next post navigation.
<XMarkBlogNavigation
:previous="{ title: 'Previous Post', slug: 'prev-post' }"
:next="{ title: 'Next Post', slug: 'next-post' }"
base-path="/blog"
/>XMarkBlogCTA
In-article newsletter signup.
<XMarkBlogCTA
title="Enjoyed this article?"
description="Subscribe for more content."
variant="featured"
@submit="handleSubmit"
/>UI Elements
XMarkBadge
Trust and feature badges with presets.
<XMarkBadge preset="no-credit-card" variant="subtle" />
<XMarkBadge preset="cancel-anytime" />
<XMarkBadge icon="i-lucide-star" label="5-star rated" />| Prop | Type | Default | Description |
|------|------|---------|-------------|
| preset | String | '' | no-credit-card, cancel-anytime, money-back, free-trial, secure |
| icon | String | '' | Custom icon (if no preset) |
| label | String | '' | Custom label (if no preset) |
| variant | String | 'default' | default, subtle, outline |
XMarkGlassCard
Glassmorphism card with blur effect.
<XMarkGlassCard :blur="10" :opacity="0.1">
<h3>Card Content</h3>
</XMarkGlassCard>XMarkPromoCard
Promotional card with image and CTA.
<XMarkPromoCard
title="Special Offer"
description="Get 50% off your first month."
image="/promo.jpg"
cta-text="Claim Offer"
cta-link="/pricing"
/>XMarkGlowDivider
Glowing section divider.
<XMarkGlowDivider color="primary" :intensity="0.5" />XMarkPatternBg
SVG background patterns.
<XMarkPatternBg pattern="dots" :opacity="0.05" />| Prop | Type | Default | Description |
|------|------|---------|-------------|
| pattern | String | 'dots' | dots, grid, diagonal, topography, circuit, waves |
| opacity | Number | 0.05 | Pattern opacity |
XMarkSectionStitch
Section divider shapes.
<XMarkSectionStitch shape="wave" position="top" :flip="false" />| Prop | Type | Default | Description |
|------|------|---------|-------------|
| shape | String | 'wave' | angle, wave, curve, triangle, zigzag |
| position | String | 'bottom' | top, bottom |
| flip | Boolean | false | Flip horizontally |
Modals & Overlays
XMarkVideoModal
Video lightbox supporting YouTube, Vimeo, and direct URLs.
<XMarkVideoModal
v-model="showVideo"
url="https://www.youtube.com/watch?v=dQw4w9WgXcQ"
title="Product Demo"
/>XMarkImageLightbox
Image gallery lightbox with keyboard navigation.
<XMarkImageLightbox
v-model="showLightbox"
:images="[
{ src: '/gallery/1.jpg', alt: 'Image 1', caption: 'First image' },
{ src: '/gallery/2.jpg', alt: 'Image 2' },
]"
:start-index="0"
/>XMarkFeatureModal
Feature detail modal.
<XMarkFeatureModal
v-model="showFeature"
:feature="{
icon: 'i-lucide-zap',
title: 'Lightning Fast',
description: 'Detailed description...',
image: '/features/speed.jpg',
}"
/>Notifications & Banners
XMarkAnnouncementBar
Dismissible top announcement banner.
<XMarkAnnouncementBar
message="New feature available!"
link-text="Learn more"
link-url="/features"
variant="primary"
:dismissible="true"
@dismiss="handleDismiss"
/>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| message | String | required | Announcement text |
| linkText | String | '' | Optional link text |
| linkUrl | String | '' | Optional link URL |
| variant | String | 'primary' | primary, neutral, success, warning, error |
| dismissible | Boolean | true | Show dismiss button |
XMarkCookieBanner
Cookie consent banner.
<XMarkCookieBanner
message="We use cookies to improve your experience."
accept-text="Accept All"
decline-text="Decline"
:show-preferences="true"
@accept="handleAccept"
@decline="handleDecline"
@preferences="handlePreferences"
/>XMarkCookieToast
Cookie toast notification.
<XMarkCookieToast
message="We use cookies"
@accept="handleAccept"
/>XMarkPrivacyCookieConsent
Consent-aware cookie banner + preferences modal that auto-fires tracking scripts. Drop your GTM/GA4/Clarity IDs into app.config.ts, drop <XMarkPrivacyCookieConsent /> into your layout, and the component handles banner display, consent storage, and dynamic script injection.
<!-- app.vue -->
<template>
<div>
<NuxtPage />
<XMarkPrivacyCookieConsent
policy-url="/cookies"
privacy-url="/privacy"
/>
</div>
</template>// app.config.ts
export default defineAppConfig({
xMarketing: {
tracking: {
gtmId: 'GTM-XXXXXXX',
ga4Id: 'G-XXXXXXXX',
clarityId: 'abc123def4',
// Optional escape hatch for anything else (Meta Pixel, Hotjar, etc.)
scripts: [
{
id: 'meta-pixel',
src: 'https://connect.facebook.net/en_US/fbevents.js',
category: 'marketing',
attrs: { async: '' },
},
],
autoInject: true, // default
},
},
})Scripts only fire after the visitor grants consent for the matching category (analytics or marketing). On revisit, the client plugin auto-loads stored scripts immediately — no flash of banner.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| title | String | 'We use cookies' | Banner heading |
| message | String | (see source) | Banner body text |
| acceptLabel | String | 'Accept all' | Accept button text |
| rejectLabel | String | 'Reject all' | Reject button text |
| saveLabel | String | 'Save preferences' | Save button text (modal) |
| policyLabel | String | 'Read our policy' | Policy link text |
| policyUrl | String | — | Cookie policy URL |
| privacyUrl | String | — | Privacy policy URL |
| prefsTitle | String | 'Cookie preferences' | Modal title |
| prefsDescription | String | (see source) | Modal description |
| showCustomize | Boolean | true | Show the Customize button |
| categories | Category[] | (4 standard) | Categories shown in the modal |
| storageKey | String | 'xMarketing.consent' | localStorage key |
| forceShow | Boolean | false | Force the banner open (e.g. from a "Manage cookies" link) |
| Emit | Payload | When |
|------|---------|------|
| @accept | Record<CategoryId, boolean> | Visitor accepted all |
| @reject | Record<CategoryId, boolean> | Visitor rejected all |
| @save | Record<CategoryId, boolean> | Visitor saved preferences |
Underlying composable — useConsentTracking() is auto-imported. Use it directly if you need to programmatically grant consent or read state:
const consent = useConsentTracking()
consent.acceptAll() // grant every category
consent.rejectAll() // grant only necessary
consent.hasConsent('analytics')
consent.state.value // reactive { necessary, analytics, marketing, preferences }XMarkGDPR
GDPR cookie preference modal.
<XMarkGDPR
v-model="showGDPR"
:categories="[
{ id: 'necessary', name: 'Necessary', description: 'Required for the site to work.', required: true },
{ id: 'analytics', name: 'Analytics', description: 'Help us improve.', default: false },
]"
@save="handleSavePreferences"
/>XMarkSocialProofToast
Social proof notification toasts.
<XMarkSocialProofToast
:notifications="[
{ name: 'John', location: 'New York', action: 'signed up', time: '2 minutes ago' },
]"
:interval="5000"
position="bottom-left"
/>Utilities
XMarkBackToTop
Scroll to top button.
<XMarkBackToTop :show-after="300" :smooth="true" />XMarkPopupChat
Chat widget trigger.
<XMarkPopupChat
provider="intercom"
:config="{ app_id: 'your-app-id' }"
/>Affiliate & Review Components
Removed in favour of @xenterprises/nuxt-x-affiliate.
XMarkAffiliateDisclosure, XMarkAffiliateProductCard, XMarkAffiliateProductGrid,
XMarkAffiliateProductDetail, and XMarkAffiliateComparisonTable duplicated components
the affiliate layer already ships — without its per-merchant tagging
(xAffiliateContent.merchants), impression tracking, or Product/Offer structured data.
Use <XAFDisclosure>, <XAFProductCard>, <XAFRelatedProducts>, <XAFBuyButton>,
and <XAFComparisonTable> instead.
Configuration
Configure the layer in your app.config.js:
export default defineAppConfig({
xMarketing: {
name: "X Enterprises",
config: {
markerProjectId: "your-marker-project-id", // Optional: Marker.io integration
},
},
});Testing
Unit Tests
Run unit tests with Vitest:
npm run test:unitE2E Tests
Run end-to-end tests with Playwright:
npm run test:e2eComposables
| Composable | Description |
|------------|-------------|
| useScrollReveal(options?) | Intersection Observer scroll animations. Auto-adds is-visible class to [data-reveal], .xFadeUp, .xFadeIn, .xFadeLeft, .xFadeRight, .xScale, .xFadeUp-stagger elements. |
| useStaggerReveal(selector, delay?) | Adds incremental transition-delay to [data-reveal] children inside a container. |
| useParallax(options?) | Parallax scroll effect for .xParallax[data-parallax-speed] elements. Uses requestAnimationFrame for performance. |
| useElementParallax(speed?) | Individual element parallax via template ref. Returns { elementRef }. |
| useXBlog() | Nuxt Content blog helpers (getPosts, getPostByPath, getSurround, normalizeBlogPost). Expects a blog collection and markdown under content/blog/. |
All composables are SSR-safe — they guard lifecycle hooks with getCurrentInstance() and import.meta.client checks.
Environment Variables
| Name | Required | Description |
|------|----------|-------------|
| Marker.io Project ID | No | Set via appConfig.xMarketing.config.markerProjectId (not env var) |
| Newsletter Org ID | No | Set via appConfig.xMarketing.config.emailMarketingNewsletters.organizationId |
The layer itself has no required environment variables. Blog content is file-based via Nuxt Content (not Builder.io). All site config is done via app.config.ts.
CSS Classes
Typography
| Class | Description |
|-------|-------------|
| xText-display | Display heading — clamp(3rem, 8vw, 6rem), weight 700, tight tracking |
| xText-headline | Section headline — clamp(2rem, 5vw, 3.5rem), weight 600 |
| xText-title | Card/feature title — clamp(1.25rem, 3vw, 1.75rem), weight 600 |
| xText-body | Body text — 1.125rem, line-height 1.7 |
| xText-small | Captions/labels — 0.875rem |
| xText-eyebrow | Uppercase label — 0.75rem, 0.1em tracking |
| xText-balance | Applies text-wrap: balance |
| xText-gradient | Gradient text using primary color |
Scroll Animations
| Class | Description |
|-------|-------------|
| xFadeUp | Fade in + slide up (24px) on scroll |
| xFadeIn | Simple fade in on scroll |
| xFadeLeft | Fade in from left (-24px) on scroll |
| xFadeRight | Fade in from right (24px) on scroll |
| xScale | Scale in (0.95 → 1) on scroll |
| xFadeUp-stagger | Stagger children with 100ms delay increments (up to 6 children) |
| [data-reveal] | Legacy fade up (40px) on scroll |
Hover Effects
| Class | Description |
|-------|-------------|
| xHover-lift | Lift up 4px + shadow on hover |
| xHover-grow | Scale to 1.02 on hover |
| xHover-glow | Primary color glow shadow on hover |
| xHover-glow-neutral | Neutral glow shadow on hover |
| xHover-glow-subtle | Subtle glow shadow on hover |
| xHover-zoom | Zoom child <img> to 1.08 on hover |
Glass & Glow
| Class | Description |
|-------|-------------|
| xGlass | Glassmorphism — 20px blur, white/70 bg, border, shadow |
| xGlass-subtle | Light glassmorphism — 8px blur |
| xGlass-heavy | Heavy glassmorphism — 40px blur |
| xGlow-divider | Glowing horizontal divider line |
| xGlow-border | Glowing gradient border |
| xGlow-text | Text glow shadow |
Overlays
| Class | Description |
|-------|-------------|
| xOverlay-light | 30% black overlay |
| xOverlay-heavy | 60% black overlay |
| xOverlay-gradient | Bottom-to-top gradient overlay |
| xOverlay-vignette | Radial vignette overlay |
Parallax
| Class | Description |
|-------|-------------|
| xParallax | JS-driven parallax (use with data-parallax-speed="0.3") |
| xParallax-bg | CSS-only parallax (background-attachment: fixed) |
All animations respect prefers-reduced-motion: reduce.
How It Works
The layer provides a complete marketing website toolkit built on Nuxt UI v4:
Component Registration: All components in
app/components/X/Mark/are auto-imported by Nuxt with theXMarkprefix (e.g.,X/Mark/Hero/index.vue→<XMarkHero>). Branded X Enterprises components live underX/X/with theXXprefix.Design System:
app/assets/css/x-marketing.cssdefines CSS custom properties for typography scale, spacing, colors, shadows, and motion. It uses Tailwind CSS v4 with@theme staticfor custom color palettes ("brand" and "deep"). Dark mode overrides use.darkselector.Animations: A client-side plugin (
marketing.client.ts) auto-initializesuseScrollReveal()anduseParallax()globally on mount. Components that need scroll animations can also calluseScrollReveal()directly.Configuration:
app.config.tsprovides thexMarketingnamespace with header/footer/blog configuration and Nuxt UI theme overrides. Consumer apps merge their ownapp.config.tsto customize.Type System:
app/types/marketing.d.tsexports interfaces for all data structures (BlogPost, Feature, PricingPlan, Testimonial, etc.) used across components.
Layer Architecture
| Path | Purpose |
|------|---------|
| nuxt.config.ts | Registers @nuxt/ui + @nuxt/content, loads CSS, enables SSR |
| content.config.ts | Default Nuxt Content blog collection schema |
| app/app.config.ts | Default xMarketing config + Nuxt UI theme overrides + type augmentation |
| app/app.vue | Default app shell with navbar, newsletter, footer (override in consumer app) |
| app/assets/css/x-marketing.css | Full design system: colors, typography, animations, effects |
| app/components/X/Mark/ | Generic marketing components (auto-imported as XMark*) |
| app/components/X/X/ | X Enterprises branded components (auto-imported as XX*) |
| app/components/X/Footer/ | Legacy footer components |
| app/components/X/Header/ | Legacy header components |
| app/composables/ | Composables: useScrollReveal, useParallax, useXBlog |
| app/plugins/marketing.client.ts | Client plugin: auto-initializes scroll/parallax globally |
| app/types/marketing.d.ts | TypeScript interfaces for all data structures |
| app/pages/blog/ | Default blog index + slug detail (Nuxt Content, not Builder.io) |
Blog (Nuxt Content)
Breaking (Builder.io removed): default /blog pages no longer call the Builder CDN.
Posts come from markdown in the consumer app.
- Peer-install
@nuxt/contentandbetter-sqlite3(already required by Content v3). - Ensure the layer is extended so
content.config.tsdefines theblogcollection (or copy/adapt it). - Add posts under
content/blog/*.md:
---
title: Getting Started
description: First post on the marketing site.
date: 2025-01-15
author: Tim
image: /blog/cover.jpg
category: Tutorial
tags: [nuxt, marketing]
published: true
readingTime: 5
---
## Hello
Markdown body rendered via `ContentRenderer`.useXBlog().getPosts()/getPostByPath()feedXMarkBlogListandXMarkBlogDetail.
Overriding in Consumer Apps
- nuxt.config.ts: Consumer config merges with layer config. Add modules, runtime config, etc.
- app.config.ts: Deep-merges with layer defaults. Set
xMarketing.header,xMarketing.footer, etc. - app.vue: Override entirely by creating your own
app.vuein the consumer app. - Pages: Consumer pages take precedence. Override
/blogby creatingpages/blog/index.vue. - Content: Own the markdown under
content/blog/; do not put secrets in frontmatter.
License
UNLICENSED - Proprietary to X Enterprises.
