npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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-marketing

Add 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 shell
  • xMarketing.footer.active: false — removes the default footer
  • xMarketing.consent.active: false — removes the cookie-consent banner (tracking scripts then never fire)
  • xMarketing.blog.active: false — the shipped /blog pages 404
  • Replace the whole shell by shipping your own app/app.vue; replace any page by shipping the same path under app/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:unit

E2E Tests

Run end-to-end tests with Playwright:

npm run test:e2e

Composables

| 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:

  1. Component Registration: All components in app/components/X/Mark/ are auto-imported by Nuxt with the XMark prefix (e.g., X/Mark/Hero/index.vue → <XMarkHero>). Branded X Enterprises components live under X/X/ with the XX prefix.

  2. Design System: app/assets/css/x-marketing.css defines CSS custom properties for typography scale, spacing, colors, shadows, and motion. It uses Tailwind CSS v4 with @theme static for custom color palettes ("brand" and "deep"). Dark mode overrides use .dark selector.

  3. Animations: A client-side plugin (marketing.client.ts) auto-initializes useScrollReveal() and useParallax() globally on mount. Components that need scroll animations can also call useScrollReveal() directly.

  4. Configuration: app.config.ts provides the xMarketing namespace with header/footer/blog configuration and Nuxt UI theme overrides. Consumer apps merge their own app.config.ts to customize.

  5. Type System: app/types/marketing.d.ts exports 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.

  1. Peer-install @nuxt/content and better-sqlite3 (already required by Content v3).
  2. Ensure the layer is extended so content.config.ts defines the blog collection (or copy/adapt it).
  3. 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`.
  1. useXBlog().getPosts() / getPostByPath() feed XMarkBlogList and XMarkBlogDetail.

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.vue in the consumer app.
  • Pages: Consumer pages take precedence. Override /blog by creating pages/blog/index.vue.
  • Content: Own the markdown under content/blog/; do not put secrets in frontmatter.

License

UNLICENSED - Proprietary to X Enterprises.