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.1.2

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

Basic App Layout

Create your app.vue with Navbar, Newsletter, and Footer:

<template>
  <div class="min-h-screen flex flex-col">
    <XMarkNavbar
      :links="navLinks"
      :logo-light="'/logo-white.svg'"
      :logo-dark="'/logo-dark.svg'"
      logo-alt="Company"
    >
      <template #actions>
        <UButton variant="ghost" color="neutral">Sign In</UButton>
        <UButton color="primary">Get Started</UButton>
      </template>
    </XMarkNavbar>

    <main class="flex-grow">
      <NuxtPage />
    </main>

    <XMarkSection bg="subtle" padding="lg">
      <XMarkNewsletter
        title="Stay in the loop"
        description="Get the latest updates delivered to your inbox."
        @submit="handleNewsletterSubmit"
      />
    </XMarkSection>

    <XMarkFooter
      :logo="'/logo-dark.svg'"
      description="Building the future of modern software."
      :social="socialLinks"
      :columns="footerColumns"
      :legal-links="legalLinks"
    />
  </div>
</template>

<script setup>
const navLinks = [
  { label: "Features", to: "/#features" },
  { label: "Pricing", to: "/pricing" },
  { label: "Blog", to: "/blog" },
];

const socialLinks = [
  { name: "Twitter", href: "https://twitter.com", icon: "i-lucide-twitter" },
  { name: "GitHub", href: "https://github.com", icon: "i-lucide-github" },
];

const footerColumns = [
  {
    title: "Product",
    links: [
      { label: "Features", to: "/#features" },
      { label: "Pricing", to: "/pricing" },
    ],
  },
  {
    title: "Company",
    links: [
      { label: "About", to: "/about" },
      { label: "Blog", to: "/blog" },
    ],
  },
];

const legalLinks = [
  { label: "Privacy Policy", to: "/privacy" },
  { label: "Terms of Service", to: "/terms" },
];

const handleNewsletterSubmit = (email) => {
  console.log("Newsletter signup:", email);
};
</script>

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

XMarkNavbar

Fixed navigation header with transparent-to-solid scroll transition.

<XMarkNavbar
  :links="[{ label: 'Features', to: '/#features' }]"
  logo-light="/logo-white.svg"
  logo-dark="/logo-dark.svg"
  logo-alt="Company"
  :transparent="true"
  :scroll-threshold="100"
>
  <template #actions>
    <UButton variant="ghost">Sign In</UButton>
    <UButton color="primary">Get Started</UButton>
  </template>
</XMarkNavbar>

| Prop | Type | Default | Description | |------|------|---------|-------------| | links | Array | [] | Navigation links { label, to } | | logoLight | String | '' | Logo for transparent/dark state | | logoDark | String | '' | Logo for solid/light state | | logoAlt | String | 'Logo' | Logo alt text | | 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 |

XMarkFooter

Full footer with brand, link columns, social icons, and newsletter.

<XMarkFooter
  logo="/logo.svg"
  logo-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' }]"
  :show-newsletter="true"
  @newsletter-submit="handleSubmit"
/>

| Prop | Type | Default | Description | |------|------|---------|-------------| | logo | String | '' | Logo image URL | | 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 | | showNewsletter | Boolean | false | Show newsletter section |

XMarkLegalFooter

Minimal footer with copyright and legal links only.

<XMarkLegalFooter
  company-name="X Enterprises"
  :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"
/>

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

For affiliate marketing, product review sites, and "best X" roundup posts.

XMarkAffiliateDisclosure

FTC/Amazon-required affiliate disclosure banner. Three variants for different placements.

<!-- Subtle bar (default) -->
<XMarkAffiliateDisclosure />

<!-- Full-width banner at top of page -->
<XMarkAffiliateDisclosure variant="banner" :dismissible="true" />

<!-- Inline inside blog post prose -->
<XMarkAffiliateDisclosure variant="inline" />

| Prop | Type | Default | Description | |------|------|---------|-------------| | text | String | Amazon associate disclosure | Disclosure text | | variant | String | 'subtle' | subtle, banner, inline | | icon | String | 'i-lucide-info' | Icon name | | dismissible | Boolean | false | Show dismiss button | | storageKey | String | 'affiliate-disclosure-dismissed' | localStorage key |

XMarkAffiliateProductCard

Product card with affiliate buy button, star rating, price, pros/cons. Three layout variants.

<XMarkAffiliateProductCard
  :product="{
    name: 'Sony WH-1000XM5',
    image: '/products/sony-wh1000xm5.jpg',
    price: '$279.99',
    originalPrice: '$349.99',
    rating: 4.8,
    reviewCount: 12400,
    pros: ['Best-in-class ANC', '30hr battery', 'Foldable design'],
    cons: ['No IP rating', 'Expensive'],
    affiliateUrl: 'https://amazon.com/dp/B09XS7JWHH',
    affiliateTag: 'mysite-20',
    badge: '#1 Pick',
  }"
  layout="card"
  button-label="Check Price on Amazon"
/>

| Prop | Type | Default | Description | |------|------|---------|-------------| | product | Object | sample product | Product data { name, image?, price?, originalPrice?, rating?, reviewCount?, pros?, cons?, affiliateUrl?, affiliateTag?, badge? } | | layout | String | 'card' | card (vertical), row (horizontal), compact (one-line) | | showPros | Boolean | true | Show pros list | | showCons | Boolean | false | Show cons list | | showRating | Boolean | true | Show star rating | | showPrice | Boolean | true | Show price | | buttonLabel | String | 'Check Price on Amazon' | CTA button text | | buttonIcon | String | 'i-lucide-external-link' | CTA button icon | | disclosure | String | '' | Small disclosure text below button |

XMarkAffiliateProductGrid

Grid of product cards for "best X" roundup posts. Automatically injects rank badges.

<XMarkAffiliateProductGrid
  :products="topHeadphones"
  layout="grid"
  :columns="3"
  :show-ranking="true"
  disclosure="This page contains affiliate links."
  button-label="Check Price on Amazon"
/>

| Prop | Type | Default | Description | |------|------|---------|-------------| | products | Array | [] | Array of product objects | | layout | String | 'grid' | grid or list | | columns | Number | 3 | Grid columns: 2 or 3 | | showRanking | Boolean | true | Inject #1/#2/#3 badges on first 3 products | | disclosure | String | '' | Disclosure shown once at top | | buttonLabel | String | 'Check Price on Amazon' | CTA label for all cards | | cardLayout | String | 'card' | Card layout variant |

XMarkAffiliateProductDetail

Full product review page layout — image gallery, editorial score, pros/cons table, specs, sticky buy box.

<XMarkAffiliateProductDetail
  :product="{
    name: 'Sony WH-1000XM5 Review',
    images: ['/img/sony-1.jpg', '/img/sony-2.jpg'],
    price: '$279.99',
    originalPrice: '$349.99',
    rating: 4.8,
    reviewCount: 12400,
    pros: ['Best-in-class ANC', '30hr battery'],
    cons: ['No IP rating', 'Expensive'],
    specs: { 'Driver Size': '30mm', 'Battery': '30 hours', 'Weight': '250g' },
    description: 'The WH-1000XM5 sets the bar for noise-cancelling headphones.',
    verdict: 'The best ANC headphones money can buy in 2024.',
    affiliateUrl: 'https://amazon.com/dp/B09XS7JWHH',
    affiliateTag: 'mysite-20',
  }"
  :score="9.2"
  :has-buy-box="true"
  :has-specs="true"
  :has-pros-cons-table="true"
  disclosure="This page contains affiliate links."
>
  <template #after-verdict>
    <!-- Additional content after verdict -->
  </template>
  <template #sidebar>
    <!-- Additional sidebar widgets -->
  </template>
</XMarkAffiliateProductDetail>

| Prop | Type | Default | Description | |------|------|---------|-------------| | product | Object | sample product | Full product data including images[], specs{}, verdict | | score | Number | null | Editorial score 0–10 (shown as circular badge) | | hasBuyBox | Boolean | true | Show sticky buy box sidebar | | hasSpecs | Boolean | true | Show specs table | | hasProsConsTable | Boolean | true | Show pros/cons two-column table | | disclosure | String | '' | Affiliate disclosure text |

Slots: buy-box, after-verdict, sidebar

XMarkAffiliateComparisonTable

Side-by-side product comparison table for "X vs Y" posts. Highlights best values per row.

<XMarkAffiliateComparisonTable
  :products="[
    {
      name: 'Sony WH-1000XM5',
      image: '/img/sony.jpg',
      price: '$279.99',
      affiliateUrl: 'https://amazon.com/dp/B09XS7JWHH',
      affiliateTag: 'mysite-20',
      specs: { price: '$279.99', rating: '4.8/5', battery: '30 hours', weight: '250g' },
    },
    {
      name: 'Bose QC45',
      image: '/img/bose.jpg',
      price: '$249.99',
      affiliateUrl: 'https://amazon.com/dp/B098FKXT8L',
      affiliateTag: 'mysite-20',
      specs: { price: '$249.99', rating: '4.6/5', battery: '24 hours', weight: '238g' },
    },
  ]"
  :specs="[
    { label: 'Price', key: 'price' },
    { label: 'Rating', key: 'rating' },
    { label: 'Battery Life', key: 'battery' },
    { label: 'Weight', key: 'weight' },
  ]"
  :show-buy-buttons="true"
  :highlight-best="true"
  button-label="Buy on Amazon"
  disclosure="This page contains affiliate links."
/>

| Prop | Type | Default | Description | |------|------|---------|-------------| | products | Array | 2 sample products | 2–4 products with specs object | | specs | Array | price/rating/battery | [{ label, key }] spec rows | | showBuyButtons | Boolean | true | Show buy buttons in last row | | highlightBest | Boolean | true | Highlight best value per row (lowest price, highest rating) | | buttonLabel | String | 'Buy on Amazon' | Buy button label | | disclosure | String | '' | Disclosure shown above table |


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() | Blog data fetching composable. Reads runtimeConfig.public.apiURL. |

All composables are SSR-safe — they guard lifecycle hooks with getCurrentInstance() and import.meta.client checks.


Environment Variables

| Name | Required | Description | |------|----------|-------------| | NUXT_PUBLIC_API_URL | No | Base API URL for blog composable (used by useXBlog) | | 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. All configuration 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, loads CSS, enables SSR and devtools | | 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/ | Default blog pages (index + detail with slug routing) |

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.

License

UNLICENSED - Proprietary to X Enterprises.