PageCard

GitHub
A pre-styled card component that displays a title, description and optional link.

Usage

The PageCard component provides a flexible way to display content in a card with an illustration in the default slot.

UnoCSS
Pohon UI integrates with latest UnoCSS, bringing significant improvements.
Use the PageGrid, PageColumns or PageList components to display multiple PageCard.

Title

Use the title prop to set the title of the card.

UnoCSS
<template>
  <PPageCard title="UnoCSS" />
</template>
The title is rendered as a div by default. Use the as prop with an object to render it as a heading, for example :as="{ title: 'h3' }". You can also set the root element this way: :as="{ root: 'section', title: 'h3' }".

Description

Use the description prop to set the description of the card.

UnoCSS
Pohon UI integrates with latest UnoCSS, bringing significant improvements.
<template>
  <PPageCard
    title="UnoCSS"
    description="Pohon UI integrates with latest UnoCSS, bringing significant improvements."
  />
</template>

Icon

Use the icon prop to set the icon of the card.

UnoCSS
Pohon UI integrates with latest UnoCSS, bringing significant improvements.
<template>
  <PPageCard
    title="UnoCSS"
    description="Pohon UI integrates with latest UnoCSS, bringing significant improvements."
    icon="i-simple-icons-unocss"
  />
</template>

You can pass any property from the <NuxtLink> component such as to, target, rel, etc.

UnoCSS
Pohon UI integrates with latest UnoCSS, bringing significant improvements.
<template>
  <PPageCard
    title="UnoCSS"
    description="Pohon UI integrates with latest UnoCSS, bringing significant improvements."
    icon="i-simple-icons-unocss"
    to="https://unocss.dev/"
    target="_blank"
  />
</template>

Variant

Use the variant prop to change the style of the card.

UnoCSS
Pohon UI integrates with latest UnoCSS, bringing significant improvements.
<template>
  <PPageCard
    title="UnoCSS"
    description="Pohon UI integrates with latest UnoCSS, bringing significant improvements."
    icon="i-simple-icons-unocss"
    to="https://unocss.dev/"
    target="_blank"
    variant="soft"
  />
</template>
You can apply the light or dark class to the links slot when using the solid variant to reverse the colors.

Orientation

Use the orientation prop to change the orientation with the default slot. Defaults to vertical.

UnoCSS
Pohon UI integrates with latest UnoCSS, bringing significant improvements.
<template>
  <PPageCard
    title="UnoCSS"
    description="Pohon UI integrates with latest UnoCSS, bringing significant improvements."
    icon="i-simple-icons-unocss"
    orientation="horizontal"
  >
    <PIcon name="i-simple-icons-unocss" class="size-24" />
  </PPageCard>
</template>

Reverse

Use the reverse prop to reverse the orientation of the default slot.

UnoCSS
Pohon UI integrates with latest UnoCSS, bringing significant improvements.
<template>
  <PPageCard
    title="UnoCSS"
    description="Pohon UI integrates with latest UnoCSS, bringing significant improvements."
    icon="i-simple-icons-unocss"
    orientation="horizontal"
    reverse
  >
    <PIcon name="i-simple-icons-unocss" class="size-24" />
  </PPageCard>
</template>

Highlight

Use the highlight and highlight-color props to display a highlighted border around the card.

UnoCSS
Pohon UI integrates with latest UnoCSS, bringing significant improvements.
<template>
  <PPageCard
    title="UnoCSS"
    description="Pohon UI integrates with latest UnoCSS, bringing significant improvements."
    icon="i-simple-icons-unocss"
    orientation="horizontal"
    highlight
    highlight-color="primary"
  >
    <PIcon name="i-simple-icons-unocss" class="size-24" />
  </PPageCard>
</template>

Spotlight

Use the spotlight and spotlight-color props to display a spotlight effect that follows your mouse cursor and highlights borders on hover.

The spotlight effect will take over hover effects when using a to prop. It's best to use it with the outline variant.
UnoCSS
Pohon UI integrates with latest UnoCSS, bringing significant improvements.
<template>
  <PPageCard
    title="UnoCSS"
    description="Pohon UI integrates with latest UnoCSS, bringing significant improvements."
    icon="i-simple-icons-unocss"
    orientation="horizontal"
    spotlight
    spotlight-color="primary"
  >
    <PIcon name="i-simple-icons-unocss" class="size-24" />
  </PPageCard>
</template>
You can also customize the color and size by using the --spotlight-color and --spotlight-size CSS variables:
<template>
  <PPageCard
    spotlight
    class="[--spotlight-color:var(--ui-color-error)] [--spotlight-size:200px]"
  />
</template>

Examples

As a testimonial

Use the User component in the header or footer slot to make the card look like a testimonial.

“Nuxt on Cloudflare infra with minimal effort - this is huge!”
Evan You

Evan You

Author of Vue.js and Vite

<script setup lang="ts">
const testimonial = ref({
  user: {
    name: 'Evan You',
    description: 'Author of Vue.js and Vite',
    avatar: {
      src: 'https://avatars.githubusercontent.com/u/499550?v=4',
      alt: 'Evan You',
      loading: 'lazy' as const,
    },
  },
  quote: '“Nuxt on Cloudflare infra with minimal effort - this is huge!”',
});
</script>

<template>
  <PPageCard
    :description="testimonial.quote"
    class="w-60"
  >
    <template #footer>
      <PUser v-bind="testimonial.user" />
    </template>
  </PPageCard>
</template>
<script setup lang="ts">
import { ref } from 'vue'

const testimonial = ref({
  user: {
    name: 'Evan You',
    description: 'Author of Vue.js and Vite',
    avatar: {
      src: 'https://avatars.githubusercontent.com/u/499550?v=4',
      alt: 'Evan You',
      loading: 'lazy' as const,
    },
  },
  quote: '“Nuxt on Cloudflare infra with minimal effort - this is huge!”',
});
</script>

<template>
  <PPageCard
    :description="testimonial.quote"
    class="w-60"
  >
    <template #footer>
      <PUser v-bind="testimonial.user" />
    </template>
  </PPageCard>
</template>
You can use the PageColumns component to display multiple PageCard in a multi-column layout.

API

Props

Prop Default Type
as
'div'
any

The element or component this component should render as.

icon
any

The icon displayed above the title.

title
string
description
string
orientation
'vertical'
"horizontal" | "vertical"

The orientation of the page card.

reverse
false
boolean

Reverse the order of the default slot.

highlight
boolean

Display a line around the page card.

highlightColor
'primary'
"primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral"
spotlight
boolean

Display a spotlight effect that follows your mouse cursor and highlights borders on hover.

spotlightColor
'primary'
"primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral"
variant
'outline'
"solid" | "outline" | "soft" | "subtle" | "ghost" | "naked"
to
string | et | X
target
null | "_blank" | "_parent" | "_self" | "_top" | string
ui
{ root?: SlotClass; spotlight?: SlotClass; container?: SlotClass; wrapper?: SlotClass; header?: SlotClass; body?: SlotClass; footer?: SlotClass; leading?: SlotClass; leadingIcon?: SlotClass; title?: SlotClass; description?: SlotClass; }

Slots

Slot Type
header
{}
body
{}
leading
{ ui: object; }
title
{}
description
{}
footer
{}
default
{}

Theme

app.config.ts
export default defineAppConfig({
  ui: {
    pageCard: {
      slots: {
        root: 'relative flex rounded-lg',
        spotlight: 'absolute inset-0 rounded-inherit pointer-events-none bg-background/90',
        container: 'relative flex flex-col flex-1 lg:grid gap-x-8 gap-y-4 p-4 sm:p-6',
        wrapper: 'flex flex-col flex-1 items-start',
        header: 'mb-4',
        body: 'flex-1',
        footer: 'pt-4 mt-auto',
        leading: 'inline-flex items-center mb-2.5',
        leadingIcon: 'size-5 shrink-0 color-primary',
        title: 'text-base text-pretty font-600 color-text-highlighted',
        description: 'text-[15px] text-pretty'
      },
      variants: {
        orientation: {
          horizontal: {
            container: 'lg:grid-cols-2 lg:items-center'
          },
          vertical: {
            container: ''
          }
        },
        reverse: {
          true: {
            wrapper: 'order-last'
          }
        },
        variant: {
          solid: {
            root: 'bg-background-inverted color-text-inverted',
            title: 'color-text-inverted',
            description: 'color-text-dimmed'
          },
          outline: {
            root: 'bg-background ring ring-border',
            description: 'color-text-muted'
          },
          soft: {
            root: 'bg-background-elevated/50',
            description: 'color-text-toned'
          },
          subtle: {
            root: 'bg-background-elevated/50 ring ring-border',
            description: 'color-text-toned'
          },
          ghost: {
            description: 'color-text-muted'
          },
          naked: {
            container: 'pohon:p-0 pohon:sm:p-0',
            description: 'color-text-muted'
          }
        },
        to: {
          true: {
            root: [
              'outline-primary/25 has-[>a:focus-visible]:outline-3',
              'transition'
            ]
          }
        },
        title: {
          true: {
            description: 'mt-1'
          }
        },
        highlight: {
          true: {
            root: 'ring-2'
          }
        },
        highlightColor: {
          primary: '',
          secondary: '',
          success: '',
          info: '',
          warning: '',
          error: '',
          neutral: ''
        },
        spotlight: {
          true: {
            root: '[--spotlight-size:400px] before:rounded-inherit before:bg-[radial-gradient(var(--spotlight-size)_var(--spotlight-size)_at_calc(var(--spotlight-x,0px))_calc(var(--spotlight-y,0px)),var(--spotlight-color),transparent_70%)] before:pointer-events-none before:content-empty before:absolute before:-inset-px'
          }
        },
        spotlightColor: {
          primary: '',
          secondary: '',
          success: '',
          info: '',
          warning: '',
          error: '',
          neutral: ''
        }
      },
      compoundVariants: [
        {
          variant: 'solid',
          to: true,
          class: {
            root: 'hover:bg-background-inverted/90'
          }
        },
        {
          variant: 'outline',
          to: true,
          class: {
            root: 'hover:bg-background-elevated/50'
          }
        },
        {
          variant: 'soft',
          to: true,
          class: {
            root: 'hover:bg-background-elevated'
          }
        },
        {
          variant: 'subtle',
          to: true,
          class: {
            root: 'hover:bg-background-elevated'
          }
        },
        {
          variant: 'subtle',
          to: true,
          highlight: false,
          class: {
            root: 'hover:ring-border-accented'
          }
        },
        {
          variant: [
            'outline',
            'subtle'
          ],
          to: true,
          highlight: false,
          class: {
            root: 'has-[>a:focus-visible]:ring-primary'
          }
        },
        {
          variant: 'ghost',
          to: true,
          class: {
            root: 'hover:bg-background-elevated/50'
          }
        },
        {
          highlightColor: 'primary',
          highlight: true,
          class: {
            root: 'pohon:ring-primary'
          }
        },
        {
          highlightColor: 'neutral',
          highlight: true,
          class: {
            root: 'ring-border-inverted'
          }
        },
        {
          spotlightColor: 'primary',
          spotlight: true,
          class: {
            root: '[--spotlight-color:var(--ui-primary)]'
          }
        },
        {
          spotlightColor: 'secondary',
          spotlight: true,
          class: {
            root: '[--spotlight-color:var(--ui-secondary)]'
          }
        },
        {
          spotlightColor: 'success',
          spotlight: true,
          class: {
            root: '[--spotlight-color:var(--ui-success)]'
          }
        },
        {
          spotlightColor: 'info',
          spotlight: true,
          class: {
            root: '[--spotlight-color:var(--ui-info)]'
          }
        },
        {
          spotlightColor: 'warning',
          spotlight: true,
          class: {
            root: '[--spotlight-color:var(--ui-warning)]'
          }
        },
        {
          spotlightColor: 'error',
          spotlight: true,
          class: {
            root: '[--spotlight-color:var(--ui-error)]'
          }
        },
        {
          spotlightColor: 'neutral',
          spotlight: true,
          class: {
            root: '[--spotlight-color:var(--ui-color-bg-inverted)]'
          }
        }
      ],
      defaultVariants: {
        variant: 'outline',
        highlightColor: 'primary',
        spotlightColor: 'primary'
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from 'pohon-ui/vite'

export default defineConfig({
  plugins: [
    vue(),
    ui({
      ui: {
        pageCard: {
          slots: {
            root: 'relative flex rounded-lg',
            spotlight: 'absolute inset-0 rounded-inherit pointer-events-none bg-background/90',
            container: 'relative flex flex-col flex-1 lg:grid gap-x-8 gap-y-4 p-4 sm:p-6',
            wrapper: 'flex flex-col flex-1 items-start',
            header: 'mb-4',
            body: 'flex-1',
            footer: 'pt-4 mt-auto',
            leading: 'inline-flex items-center mb-2.5',
            leadingIcon: 'size-5 shrink-0 color-primary',
            title: 'text-base text-pretty font-600 color-text-highlighted',
            description: 'text-[15px] text-pretty'
          },
          variants: {
            orientation: {
              horizontal: {
                container: 'lg:grid-cols-2 lg:items-center'
              },
              vertical: {
                container: ''
              }
            },
            reverse: {
              true: {
                wrapper: 'order-last'
              }
            },
            variant: {
              solid: {
                root: 'bg-background-inverted color-text-inverted',
                title: 'color-text-inverted',
                description: 'color-text-dimmed'
              },
              outline: {
                root: 'bg-background ring ring-border',
                description: 'color-text-muted'
              },
              soft: {
                root: 'bg-background-elevated/50',
                description: 'color-text-toned'
              },
              subtle: {
                root: 'bg-background-elevated/50 ring ring-border',
                description: 'color-text-toned'
              },
              ghost: {
                description: 'color-text-muted'
              },
              naked: {
                container: 'pohon:p-0 pohon:sm:p-0',
                description: 'color-text-muted'
              }
            },
            to: {
              true: {
                root: [
                  'outline-primary/25 has-[>a:focus-visible]:outline-3',
                  'transition'
                ]
              }
            },
            title: {
              true: {
                description: 'mt-1'
              }
            },
            highlight: {
              true: {
                root: 'ring-2'
              }
            },
            highlightColor: {
              primary: '',
              secondary: '',
              success: '',
              info: '',
              warning: '',
              error: '',
              neutral: ''
            },
            spotlight: {
              true: {
                root: '[--spotlight-size:400px] before:rounded-inherit before:bg-[radial-gradient(var(--spotlight-size)_var(--spotlight-size)_at_calc(var(--spotlight-x,0px))_calc(var(--spotlight-y,0px)),var(--spotlight-color),transparent_70%)] before:pointer-events-none before:content-empty before:absolute before:-inset-px'
              }
            },
            spotlightColor: {
              primary: '',
              secondary: '',
              success: '',
              info: '',
              warning: '',
              error: '',
              neutral: ''
            }
          },
          compoundVariants: [
            {
              variant: 'solid',
              to: true,
              class: {
                root: 'hover:bg-background-inverted/90'
              }
            },
            {
              variant: 'outline',
              to: true,
              class: {
                root: 'hover:bg-background-elevated/50'
              }
            },
            {
              variant: 'soft',
              to: true,
              class: {
                root: 'hover:bg-background-elevated'
              }
            },
            {
              variant: 'subtle',
              to: true,
              class: {
                root: 'hover:bg-background-elevated'
              }
            },
            {
              variant: 'subtle',
              to: true,
              highlight: false,
              class: {
                root: 'hover:ring-border-accented'
              }
            },
            {
              variant: [
                'outline',
                'subtle'
              ],
              to: true,
              highlight: false,
              class: {
                root: 'has-[>a:focus-visible]:ring-primary'
              }
            },
            {
              variant: 'ghost',
              to: true,
              class: {
                root: 'hover:bg-background-elevated/50'
              }
            },
            {
              highlightColor: 'primary',
              highlight: true,
              class: {
                root: 'pohon:ring-primary'
              }
            },
            {
              highlightColor: 'neutral',
              highlight: true,
              class: {
                root: 'ring-border-inverted'
              }
            },
            {
              spotlightColor: 'primary',
              spotlight: true,
              class: {
                root: '[--spotlight-color:var(--ui-primary)]'
              }
            },
            {
              spotlightColor: 'secondary',
              spotlight: true,
              class: {
                root: '[--spotlight-color:var(--ui-secondary)]'
              }
            },
            {
              spotlightColor: 'success',
              spotlight: true,
              class: {
                root: '[--spotlight-color:var(--ui-success)]'
              }
            },
            {
              spotlightColor: 'info',
              spotlight: true,
              class: {
                root: '[--spotlight-color:var(--ui-info)]'
              }
            },
            {
              spotlightColor: 'warning',
              spotlight: true,
              class: {
                root: '[--spotlight-color:var(--ui-warning)]'
              }
            },
            {
              spotlightColor: 'error',
              spotlight: true,
              class: {
                root: '[--spotlight-color:var(--ui-error)]'
              }
            },
            {
              spotlightColor: 'neutral',
              spotlight: true,
              class: {
                root: '[--spotlight-color:var(--ui-color-bg-inverted)]'
              }
            }
          ],
          defaultVariants: {
            variant: 'outline',
            highlightColor: 'primary',
            spotlightColor: 'primary'
          }
        }
      }
    })
  ]
})
Some colors in compoundVariants are omitted for readability. Check out the source code on GitHub.

Changelog

No recent changes