adding some more cards
This commit is contained in:
@@ -1,5 +1,119 @@
|
|||||||
# Vue Material Kit Design System
|
# MK Design System
|
||||||
|
|
||||||
This is a fork/derivative of [Creative Tim's Vue Material Kit 2](https://www.creative-tim.com/product/vue-material-kit), source found [here](https://github.com/creativetimofficial/vue-material-kit).
|
A Vue 3 + Tailwind CSS v4 design system derived from [Creative Tim's Vue Material Kit 2](https://github.com/creativetimofficial/vue-material-kit). Built to be handed off to another Claude instance for consistent UI work across projects.
|
||||||
|
|
||||||
I am going to iterate over it with an AI agent and extract styles and functionality into a design system that can be plugged in to another project. The end goal is to have a working directory than can be handed off to another AI agent that is used when building a new site to keep a consistent and clean design.
|
**30+ components** · **Design tokens in CSS** · **Roboto variable font** · **Material Icons Round**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Using in your project
|
||||||
|
|
||||||
|
### Option A — Copy components (simplest)
|
||||||
|
|
||||||
|
1. Copy `src/components/`, `src/composables/`, and `src/style.css` into your project.
|
||||||
|
|
||||||
|
2. Ensure your project has Tailwind CSS v4 with `@tailwindcss/vite`. Your `style.css` must contain the `@import "tailwindcss"` line and the full `@theme` block from this project's `src/style.css`.
|
||||||
|
|
||||||
|
3. Get the font files (4 woff2 files — see **Fonts** below) and place them in `public/fonts/`.
|
||||||
|
|
||||||
|
4. Import components directly:
|
||||||
|
```js
|
||||||
|
import MkButton from '@/components/MkButton.vue'
|
||||||
|
import MkInput from '@/components/MkInput.vue'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option B — Build as a library
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm build:lib
|
||||||
|
```
|
||||||
|
|
||||||
|
This outputs `dist/mk-design-system.es.js`, `dist/mk-design-system.cjs.js`, and `dist/style.css`.
|
||||||
|
|
||||||
|
In the consuming project:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// main.js — global registration
|
||||||
|
import { MkDesignSystem } from './mk-design-system.es.js'
|
||||||
|
import './mk-design-system.css' // design tokens + @font-face
|
||||||
|
app.use(MkDesignSystem)
|
||||||
|
```
|
||||||
|
|
||||||
|
Or import components individually:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { MkButton, MkInput, MkNavbar } from './mk-design-system.es.js'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm install
|
||||||
|
pnpm dev # dashboard + showcase at localhost:5173
|
||||||
|
pnpm build # production app build → dist/
|
||||||
|
pnpm build:lib # library build → dist/mk-design-system.*
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fonts
|
||||||
|
|
||||||
|
Place these woff2 files in `public/fonts/` before running the dev server:
|
||||||
|
|
||||||
|
```
|
||||||
|
public/fonts/roboto/
|
||||||
|
Roboto-VariableFont_wdth-wght.woff2
|
||||||
|
Roboto-Italic-VariableFont_wdth-wght.woff2
|
||||||
|
|
||||||
|
public/fonts/roboto-slab/
|
||||||
|
RobotoSlab-VariableFont_wght.woff2
|
||||||
|
|
||||||
|
public/fonts/material-icons-round/
|
||||||
|
material-icons-round-latin-400-normal.woff2
|
||||||
|
```
|
||||||
|
|
||||||
|
Download script (run from `/app`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Roboto (official Google Fonts variable build)
|
||||||
|
curl -L "https://github.com/google/fonts/raw/main/apache/roboto/Roboto%5Bwdth%2Cwght%5D.ttf" \
|
||||||
|
-o public/fonts/roboto/Roboto-VariableFont_wdth-wght.woff2
|
||||||
|
|
||||||
|
# Material Icons Round
|
||||||
|
curl -L "https://cdn.jsdelivr.net/npm/@fontsource/material-icons-round@5/files/material-icons-round-latin-400-normal.woff2" \
|
||||||
|
-o public/fonts/material-icons-round/material-icons-round-latin-400-normal.woff2
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Design reference
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `design-system.md` | Token reference, component patterns, 12 quick-start rules |
|
||||||
|
| `conformity.md` | 17-rule checklist — feed this + generated code to Claude to score conformity |
|
||||||
|
| `tokens.json` | Machine-readable design token values |
|
||||||
|
| `src/style.css` | Live source of truth — `@theme` block defines all tokens |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## For Claude
|
||||||
|
|
||||||
|
When starting a new project using this design system, provide Claude with:
|
||||||
|
|
||||||
|
1. `design-system.md` — the rules to follow
|
||||||
|
2. `conformity.md` — to score and verify generated code
|
||||||
|
3. The component list from `src/index.js` — so it knows what's available
|
||||||
|
|
||||||
|
Prompt template:
|
||||||
|
> "Use the MK Design System (design-system.md attached). Available components are listed in src/index.js. Build [feature]. After generating, score it against conformity.md."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- `MkSocialButton` renders Font Awesome brand icons (`fab fa-*`). Load Font Awesome Free 6 externally if social icons are needed.
|
||||||
|
- `MkRotatingCard` requires its parent to set an explicit height (e.g. `style="height: 22rem"`).
|
||||||
|
- `MkNavbar` scroll-blur is automatic in `transparent` mode — no JS required.
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
|
"build:lib": "BUILD_TARGET=lib vite build",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
title: { type: String, default: '' },
|
||||||
|
// Accepts HTML — e.g. "<span class='font-bold'>+15%</span> increase today"
|
||||||
|
subtitle: { type: String, default: '' },
|
||||||
|
// Timestamp label shown in footer — e.g. "updated 4 min ago"
|
||||||
|
update: { type: String, default: '' },
|
||||||
|
color: { type: String, default: 'primary' },
|
||||||
|
})
|
||||||
|
|
||||||
|
const gradientMap = {
|
||||||
|
primary: 'bg-gradient-primary shadow-primary',
|
||||||
|
secondary: 'bg-gradient-secondary shadow-secondary',
|
||||||
|
success: 'bg-gradient-success shadow-success',
|
||||||
|
warning: 'bg-gradient-warning shadow-warning',
|
||||||
|
danger: 'bg-gradient-danger shadow-danger',
|
||||||
|
info: 'bg-gradient-info shadow-info',
|
||||||
|
dark: 'bg-gradient-dark shadow-dark',
|
||||||
|
light: 'bg-gradient-light shadow-soft-sm',
|
||||||
|
}
|
||||||
|
|
||||||
|
const headerClass = computed(() => gradientMap[props.color] ?? gradientMap.primary)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="rounded-2xl bg-white shadow-soft-md overflow-visible">
|
||||||
|
<!-- Floating chart header — slot content sits inside the gradient area -->
|
||||||
|
<div class="relative -mt-6 mx-4 z-10 overflow-hidden rounded-xl px-3 py-3" :class="headerClass">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Body -->
|
||||||
|
<div class="px-4 pt-3 pb-4">
|
||||||
|
<h6 class="mb-0 font-bold text-dark">{{ title }}</h6>
|
||||||
|
<p class="text-sm text-secondary mt-0.5" v-html="subtitle" />
|
||||||
|
|
||||||
|
<div class="my-3 border-t border-gray-100" />
|
||||||
|
|
||||||
|
<div class="flex items-center gap-1 text-sm text-secondary">
|
||||||
|
<span class="material-icons text-base leading-none">schedule</span>
|
||||||
|
{{ update }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
// v-bind="$attrs" in the template passes attrs to the info wrapper, not the root
|
||||||
|
defineOptions({ inheritAttrs: false })
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
// String icon name, or { component, color, size }
|
// String icon name, or { component, color, size }
|
||||||
icon: { type: [String, Object], default: '' },
|
icon: { type: [String, Object], default: '' },
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
// { text: 'Label', value: '$53k' }
|
||||||
|
title: { type: Object, required: true },
|
||||||
|
// HTML string — e.g. "<span class='text-success'>+55%</span> than last week"
|
||||||
|
detail: { type: String, default: '' },
|
||||||
|
// { name: 'weekend', color: 'white'|color-name, background: color-name }
|
||||||
|
icon: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({ name: 'bar_chart', color: 'white', background: 'dark' }),
|
||||||
|
},
|
||||||
|
// Swap icon to the right, value to the left
|
||||||
|
directionReverse: { type: Boolean, default: false },
|
||||||
|
})
|
||||||
|
|
||||||
|
const gradientMap = {
|
||||||
|
primary: 'bg-gradient-primary shadow-primary',
|
||||||
|
secondary: 'bg-gradient-secondary shadow-secondary',
|
||||||
|
success: 'bg-gradient-success shadow-success',
|
||||||
|
warning: 'bg-gradient-warning shadow-warning',
|
||||||
|
danger: 'bg-gradient-danger shadow-danger',
|
||||||
|
info: 'bg-gradient-info shadow-info',
|
||||||
|
dark: 'bg-gradient-dark shadow-dark',
|
||||||
|
light: 'bg-gradient-light shadow-soft-sm',
|
||||||
|
}
|
||||||
|
|
||||||
|
const iconClass = computed(() => gradientMap[props.icon.background] ?? gradientMap.dark)
|
||||||
|
const iconTextClass = computed(() =>
|
||||||
|
props.icon.color === 'white' ? 'text-white' : `text-${props.icon.color}`
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="rounded-2xl bg-white shadow-soft-md">
|
||||||
|
<!-- Header: floating icon + metric -->
|
||||||
|
<div class="relative px-4 pt-2 pb-3">
|
||||||
|
<div
|
||||||
|
class="absolute -top-5 flex h-14 w-14 items-center justify-center rounded-xl transition-all"
|
||||||
|
:class="[iconClass, directionReverse ? 'right-4' : 'left-4']"
|
||||||
|
>
|
||||||
|
<span class="material-icons text-2xl opacity-90" :class="iconTextClass">
|
||||||
|
{{ icon.name }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pt-1" :class="directionReverse ? 'text-left pl-16' : 'text-right'">
|
||||||
|
<p class="mb-0 text-sm text-secondary capitalize">{{ title.text }}</p>
|
||||||
|
<h4 class="mb-0 text-xl font-bold text-dark">{{ title.value }}</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mx-4 border-t border-gray-100" />
|
||||||
|
|
||||||
|
<!-- Footer: detail text -->
|
||||||
|
<div class="px-4 py-3">
|
||||||
|
<p class="mb-0 text-sm text-secondary" v-html="detail" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
<script setup>
|
||||||
|
import { inject, computed } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
// Material icon name, e.g. 'notifications'
|
||||||
|
icon: { type: String, default: 'circle' },
|
||||||
|
// Design-system color name for the icon dot
|
||||||
|
color: { type: String, default: 'success' },
|
||||||
|
title: { type: String, required: true },
|
||||||
|
dateTime: { type: String, default: '' },
|
||||||
|
description: { type: String, default: '' },
|
||||||
|
// Pass true on the last item to hide the connector line
|
||||||
|
last: { type: Boolean, default: false },
|
||||||
|
})
|
||||||
|
|
||||||
|
const dark = inject('mk-timeline-dark', false)
|
||||||
|
|
||||||
|
// Explicit map — avoids dynamic `bg-${color}/10` which Tailwind can't scan
|
||||||
|
const dotMap = {
|
||||||
|
primary: 'bg-primary/10 text-primary',
|
||||||
|
secondary: 'bg-secondary/10 text-secondary',
|
||||||
|
success: 'bg-success/10 text-success',
|
||||||
|
warning: 'bg-warning/10 text-warning',
|
||||||
|
danger: 'bg-danger/10 text-danger',
|
||||||
|
info: 'bg-info/10 text-info',
|
||||||
|
dark: 'bg-dark/10 text-dark',
|
||||||
|
}
|
||||||
|
|
||||||
|
const dotClass = computed(() => dotMap[props.color] ?? dotMap.success)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex gap-4" :class="last ? '' : 'mb-1'">
|
||||||
|
<!-- Icon dot + vertical connector -->
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<div
|
||||||
|
class="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg"
|
||||||
|
:class="dotClass"
|
||||||
|
>
|
||||||
|
<span class="material-icons text-base leading-none">{{ icon }}</span>
|
||||||
|
</div>
|
||||||
|
<!-- Connector line — hidden on last item -->
|
||||||
|
<div v-if="!last" class="mt-1 w-px flex-1 bg-gray-200" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Content -->
|
||||||
|
<div class="pb-4" :class="last ? 'pb-1' : ''">
|
||||||
|
<h6
|
||||||
|
class="mb-0 text-sm font-bold leading-snug"
|
||||||
|
:class="dark ? 'text-white' : 'text-dark'"
|
||||||
|
>
|
||||||
|
{{ title }}
|
||||||
|
</h6>
|
||||||
|
<p class="mt-0.5 mb-0 text-xs text-secondary">{{ dateTime }}</p>
|
||||||
|
<p
|
||||||
|
v-if="description"
|
||||||
|
class="mt-2 mb-0 text-sm"
|
||||||
|
:class="dark ? 'text-white/80' : 'text-secondary'"
|
||||||
|
>
|
||||||
|
{{ description }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<script setup>
|
||||||
|
import { provide } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
title: { type: String, default: '' },
|
||||||
|
description: { type: String, default: '' },
|
||||||
|
dark: { type: Boolean, default: false },
|
||||||
|
})
|
||||||
|
|
||||||
|
// MkTimelineItem reads this to adapt its text colors
|
||||||
|
provide('mk-timeline-dark', props.dark)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="rounded-2xl shadow-soft-md h-full"
|
||||||
|
:class="dark ? 'bg-gradient-dark' : 'bg-white'"
|
||||||
|
>
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="px-4 pt-4 pb-0">
|
||||||
|
<h6 class="font-bold" :class="dark ? 'text-white' : 'text-dark'">{{ title }}</h6>
|
||||||
|
<p class="text-sm text-secondary" v-html="description" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Timeline items -->
|
||||||
|
<div class="px-4 pt-2 pb-4">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
// ── Core UI ───────────────────────────────────────────────────
|
||||||
|
export { default as MkAlert } from './components/MkAlert.vue'
|
||||||
|
export { default as MkAvatar } from './components/MkAvatar.vue'
|
||||||
|
export { default as MkBadge } from './components/MkBadge.vue'
|
||||||
|
export { default as MkButton } from './components/MkButton.vue'
|
||||||
|
export { default as MkCheckbox } from './components/MkCheckbox.vue'
|
||||||
|
export { default as MkInput } from './components/MkInput.vue'
|
||||||
|
export { default as MkPagination } from './components/MkPagination.vue'
|
||||||
|
export { default as MkPaginationItem } from './components/MkPaginationItem.vue'
|
||||||
|
export { default as MkProgress } from './components/MkProgress.vue'
|
||||||
|
export { default as MkSocialButton } from './components/MkSocialButton.vue'
|
||||||
|
export { default as MkSwitch } from './components/MkSwitch.vue'
|
||||||
|
export { default as MkTable } from './components/MkTable.vue'
|
||||||
|
export { default as MkTextArea } from './components/MkTextArea.vue'
|
||||||
|
|
||||||
|
// ── Cards ─────────────────────────────────────────────────────
|
||||||
|
export { default as MkBackgroundBlogCard } from './components/cards/MkBackgroundBlogCard.vue'
|
||||||
|
export { default as MkCenteredBlogCard } from './components/cards/MkCenteredBlogCard.vue'
|
||||||
|
export { default as MkDefaultCounterCard } from './components/cards/MkDefaultCounterCard.vue'
|
||||||
|
export { default as MkDefaultInfoCard } from './components/cards/MkDefaultInfoCard.vue'
|
||||||
|
export { default as MkDefaultReviewCard } from './components/cards/MkDefaultReviewCard.vue'
|
||||||
|
export { default as MkFilledInfoCard } from './components/cards/MkFilledInfoCard.vue'
|
||||||
|
export { default as MkHorizontalTeamCard } from './components/cards/MkHorizontalTeamCard.vue'
|
||||||
|
export { default as MkRotatingCard } from './components/cards/MkRotatingCard.vue'
|
||||||
|
export { default as MkRotatingCardBack } from './components/cards/MkRotatingCardBack.vue'
|
||||||
|
export { default as MkRotatingCardFront } from './components/cards/MkRotatingCardFront.vue'
|
||||||
|
export { default as MkTransparentBlogCard } from './components/cards/MkTransparentBlogCard.vue'
|
||||||
|
export { default as MkMiniStatisticsCard } from './components/cards/MkMiniStatisticsCard.vue'
|
||||||
|
export { default as MkChartCard } from './components/cards/MkChartCard.vue'
|
||||||
|
export { default as MkTimelineList } from './components/cards/MkTimelineList.vue'
|
||||||
|
export { default as MkTimelineItem } from './components/cards/MkTimelineItem.vue'
|
||||||
|
|
||||||
|
// ── Layout ────────────────────────────────────────────────────
|
||||||
|
export { default as MkBreadcrumbs } from './components/layout/MkBreadcrumbs.vue'
|
||||||
|
export { default as MkFooter } from './components/layout/MkFooter.vue'
|
||||||
|
export { default as MkFooterCentered } from './components/layout/MkFooterCentered.vue'
|
||||||
|
export { default as MkHeader } from './components/layout/MkHeader.vue'
|
||||||
|
export { default as MkNavbar } from './components/layout/MkNavbar.vue'
|
||||||
|
|
||||||
|
// ── Composables ───────────────────────────────────────────────
|
||||||
|
export { useCountUp } from './composables/useCountUp.js'
|
||||||
|
|
||||||
|
// ── Vue plugin (global registration) ─────────────────────────
|
||||||
|
import MkAlert from './components/MkAlert.vue'
|
||||||
|
import MkAvatar from './components/MkAvatar.vue'
|
||||||
|
import MkBadge from './components/MkBadge.vue'
|
||||||
|
import MkButton from './components/MkButton.vue'
|
||||||
|
import MkCheckbox from './components/MkCheckbox.vue'
|
||||||
|
import MkInput from './components/MkInput.vue'
|
||||||
|
import MkPagination from './components/MkPagination.vue'
|
||||||
|
import MkPaginationItem from './components/MkPaginationItem.vue'
|
||||||
|
import MkProgress from './components/MkProgress.vue'
|
||||||
|
import MkSocialButton from './components/MkSocialButton.vue'
|
||||||
|
import MkSwitch from './components/MkSwitch.vue'
|
||||||
|
import MkTable from './components/MkTable.vue'
|
||||||
|
import MkTextArea from './components/MkTextArea.vue'
|
||||||
|
import MkBackgroundBlogCard from './components/cards/MkBackgroundBlogCard.vue'
|
||||||
|
import MkCenteredBlogCard from './components/cards/MkCenteredBlogCard.vue'
|
||||||
|
import MkDefaultCounterCard from './components/cards/MkDefaultCounterCard.vue'
|
||||||
|
import MkDefaultInfoCard from './components/cards/MkDefaultInfoCard.vue'
|
||||||
|
import MkDefaultReviewCard from './components/cards/MkDefaultReviewCard.vue'
|
||||||
|
import MkFilledInfoCard from './components/cards/MkFilledInfoCard.vue'
|
||||||
|
import MkHorizontalTeamCard from './components/cards/MkHorizontalTeamCard.vue'
|
||||||
|
import MkRotatingCard from './components/cards/MkRotatingCard.vue'
|
||||||
|
import MkRotatingCardBack from './components/cards/MkRotatingCardBack.vue'
|
||||||
|
import MkRotatingCardFront from './components/cards/MkRotatingCardFront.vue'
|
||||||
|
import MkTransparentBlogCard from './components/cards/MkTransparentBlogCard.vue'
|
||||||
|
import MkMiniStatisticsCard from './components/cards/MkMiniStatisticsCard.vue'
|
||||||
|
import MkChartCard from './components/cards/MkChartCard.vue'
|
||||||
|
import MkTimelineList from './components/cards/MkTimelineList.vue'
|
||||||
|
import MkTimelineItem from './components/cards/MkTimelineItem.vue'
|
||||||
|
import MkBreadcrumbs from './components/layout/MkBreadcrumbs.vue'
|
||||||
|
import MkFooter from './components/layout/MkFooter.vue'
|
||||||
|
import MkFooterCentered from './components/layout/MkFooterCentered.vue'
|
||||||
|
import MkHeader from './components/layout/MkHeader.vue'
|
||||||
|
import MkNavbar from './components/layout/MkNavbar.vue'
|
||||||
|
|
||||||
|
const components = [
|
||||||
|
MkAlert, MkAvatar, MkBadge, MkButton, MkCheckbox, MkInput,
|
||||||
|
MkPagination, MkPaginationItem, MkProgress, MkSocialButton,
|
||||||
|
MkSwitch, MkTable, MkTextArea,
|
||||||
|
MkBackgroundBlogCard, MkCenteredBlogCard, MkDefaultCounterCard,
|
||||||
|
MkDefaultInfoCard, MkDefaultReviewCard, MkFilledInfoCard,
|
||||||
|
MkHorizontalTeamCard, MkRotatingCard, MkRotatingCardBack,
|
||||||
|
MkRotatingCardFront, MkTransparentBlogCard,
|
||||||
|
MkMiniStatisticsCard, MkChartCard, MkTimelineList, MkTimelineItem,
|
||||||
|
MkBreadcrumbs, MkFooter, MkFooterCentered, MkHeader, MkNavbar,
|
||||||
|
]
|
||||||
|
|
||||||
|
export const MkDesignSystem = {
|
||||||
|
install(app) {
|
||||||
|
components.forEach(c => app.component(c.__name, c))
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -346,7 +346,7 @@ const memberColor = { PC: 'primary', JD: 'info', SR: 'success', ML: 'warning', A
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<button class="rounded-lg bg-gradient-primary px-6 py-2.5 text-sm font-medium text-white shadow-primary transition-shadow hover:shadow-soft-md">
|
<button class="rounded-lg bg-gradient-primary px-6 py-2.5 text-sm font-medium text-white shadow-primary hover:-translate-y-0.5 active:translate-y-0 transition-all">
|
||||||
Save Changes
|
Save Changes
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+120
-1
@@ -28,6 +28,10 @@ import MkHorizontalTeamCard from '../components/cards/MkHorizontalTeamCard.vue'
|
|||||||
import MkRotatingCard from '../components/cards/MkRotatingCard.vue'
|
import MkRotatingCard from '../components/cards/MkRotatingCard.vue'
|
||||||
import MkRotatingCardFront from '../components/cards/MkRotatingCardFront.vue'
|
import MkRotatingCardFront from '../components/cards/MkRotatingCardFront.vue'
|
||||||
import MkRotatingCardBack from '../components/cards/MkRotatingCardBack.vue'
|
import MkRotatingCardBack from '../components/cards/MkRotatingCardBack.vue'
|
||||||
|
import MkMiniStatisticsCard from '../components/cards/MkMiniStatisticsCard.vue'
|
||||||
|
import MkChartCard from '../components/cards/MkChartCard.vue'
|
||||||
|
import MkTimelineList from '../components/cards/MkTimelineList.vue'
|
||||||
|
import MkTimelineItem from '../components/cards/MkTimelineItem.vue'
|
||||||
|
|
||||||
// Layout
|
// Layout
|
||||||
import MkBreadcrumbs from '../components/layout/MkBreadcrumbs.vue'
|
import MkBreadcrumbs from '../components/layout/MkBreadcrumbs.vue'
|
||||||
@@ -307,7 +311,122 @@ const breadcrumbRoutes = [
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ⑩ Counter · Review · Team ───────────────────────────── -->
|
<!-- ⑩ Dashboard Cards ───────────────────────────────────── -->
|
||||||
|
<section>
|
||||||
|
<h2 class="text-lg font-bold text-dark mb-0.5">Dashboard Cards</h2>
|
||||||
|
<p class="text-sm text-secondary mb-4">mini statistics · chart holder · timeline</p>
|
||||||
|
|
||||||
|
<!-- Mini statistics row (matches Material Dashboard layout) -->
|
||||||
|
<div class="grid gap-6 sm:grid-cols-2 xl:grid-cols-4 mb-8 mt-8">
|
||||||
|
<MkMiniStatisticsCard
|
||||||
|
:title="{ text: 'Today\'s Money', value: '$53k' }"
|
||||||
|
detail="<span class='text-success font-medium'>+55%</span> than last week"
|
||||||
|
:icon="{ name: 'weekend', color: 'white', background: 'primary' }"
|
||||||
|
/>
|
||||||
|
<MkMiniStatisticsCard
|
||||||
|
:title="{ text: 'Today\'s Users', value: '2,300' }"
|
||||||
|
detail="<span class='text-success font-medium'>+3%</span> than last month"
|
||||||
|
:icon="{ name: 'leaderboard', color: 'white', background: 'info' }"
|
||||||
|
/>
|
||||||
|
<MkMiniStatisticsCard
|
||||||
|
:title="{ text: 'New Clients', value: '3,462' }"
|
||||||
|
detail="<span class='text-danger font-medium'>-2%</span> than yesterday"
|
||||||
|
:icon="{ name: 'person', color: 'white', background: 'success' }"
|
||||||
|
/>
|
||||||
|
<MkMiniStatisticsCard
|
||||||
|
:title="{ text: 'Sales', value: '$103k' }"
|
||||||
|
detail="<span class='text-success font-medium'>+5%</span> just updated"
|
||||||
|
:icon="{ name: 'store', color: 'white', background: 'warning' }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Chart cards row -->
|
||||||
|
<div class="grid gap-6 md:grid-cols-3 mb-8 mt-12">
|
||||||
|
<!-- Bar chart placeholder -->
|
||||||
|
<MkChartCard
|
||||||
|
title="Website Views"
|
||||||
|
subtitle="Last campaign performance"
|
||||||
|
update="campaign sent 2 days ago"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
<svg viewBox="0 0 140 60" class="w-full h-20" preserveAspectRatio="none">
|
||||||
|
<rect x="8" y="30" width="12" height="30" rx="2" fill="white" opacity="0.7"/>
|
||||||
|
<rect x="28" y="42" width="12" height="18" rx="2" fill="white" opacity="0.7"/>
|
||||||
|
<rect x="48" y="20" width="12" height="40" rx="2" fill="white" opacity="0.7"/>
|
||||||
|
<rect x="68" y="10" width="12" height="50" rx="2" fill="white" opacity="0.7"/>
|
||||||
|
<rect x="88" y="35" width="12" height="25" rx="2" fill="white" opacity="0.7"/>
|
||||||
|
<rect x="108" y="22" width="12" height="38" rx="2" fill="white" opacity="0.7"/>
|
||||||
|
<rect x="128" y="15" width="12" height="45" rx="2" fill="white" opacity="0.7"/>
|
||||||
|
</svg>
|
||||||
|
</MkChartCard>
|
||||||
|
|
||||||
|
<!-- Line chart placeholder -->
|
||||||
|
<MkChartCard
|
||||||
|
title="Daily Sales"
|
||||||
|
subtitle="<span class='font-bold'>+15%</span> increase in today's sales"
|
||||||
|
update="updated 4 min ago"
|
||||||
|
color="success"
|
||||||
|
>
|
||||||
|
<svg viewBox="0 0 140 60" class="w-full h-20" preserveAspectRatio="none">
|
||||||
|
<polyline
|
||||||
|
points="0,50 18,38 35,42 52,20 70,25 88,12 105,18 122,8 140,14"
|
||||||
|
fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" opacity="0.9"
|
||||||
|
/>
|
||||||
|
<polyline
|
||||||
|
points="0,50 18,38 35,42 52,20 70,25 88,12 105,18 122,8 140,14 140,60 0,60"
|
||||||
|
fill="white" opacity="0.15"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</MkChartCard>
|
||||||
|
|
||||||
|
<!-- Line chart placeholder dark -->
|
||||||
|
<MkChartCard
|
||||||
|
title="Completed Tasks"
|
||||||
|
subtitle="Last campaign performance"
|
||||||
|
update="just updated"
|
||||||
|
color="dark"
|
||||||
|
>
|
||||||
|
<svg viewBox="0 0 140 60" class="w-full h-20" preserveAspectRatio="none">
|
||||||
|
<polyline
|
||||||
|
points="0,45 18,36 35,48 52,22 70,32 88,18 105,28 122,10 140,20"
|
||||||
|
fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" opacity="0.9"
|
||||||
|
/>
|
||||||
|
<polyline
|
||||||
|
points="0,45 18,36 35,48 52,22 70,32 88,18 105,28 122,10 140,20 140,60 0,60"
|
||||||
|
fill="white" opacity="0.15"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</MkChartCard>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Timeline card -->
|
||||||
|
<div class="grid gap-6 md:grid-cols-2">
|
||||||
|
<MkTimelineList
|
||||||
|
title="Orders Overview"
|
||||||
|
description="<span class='text-success font-medium'>+24%</span> this month"
|
||||||
|
>
|
||||||
|
<MkTimelineItem icon="notifications" color="success" title="$2,400 Design changes" date-time="22 DEC 7:20 PM" />
|
||||||
|
<MkTimelineItem icon="code" color="danger" title="New order #1832412" date-time="21 DEC 11:00 PM" />
|
||||||
|
<MkTimelineItem icon="shopping_cart" color="info" title="Server payments for April" date-time="21 DEC 9:34 PM" />
|
||||||
|
<MkTimelineItem icon="credit_card" color="warning" title="New card added for order #4395133" date-time="20 DEC 2:20 AM" />
|
||||||
|
<MkTimelineItem icon="vpn_key" color="primary" title="Unlock packages for development" date-time="18 DEC 4:54 AM" :last="true" />
|
||||||
|
</MkTimelineList>
|
||||||
|
|
||||||
|
<!-- Dark variant -->
|
||||||
|
<MkTimelineList
|
||||||
|
title="Recent Activity"
|
||||||
|
description="<span class='text-white font-medium'>12 events</span> this week"
|
||||||
|
:dark="true"
|
||||||
|
>
|
||||||
|
<MkTimelineItem icon="check_circle" color="success" title="Design system shipped" date-time="Today 10:00 AM" />
|
||||||
|
<MkTimelineItem icon="build" color="info" title="Phase 4 components merged" date-time="Yesterday 3:40 PM" />
|
||||||
|
<MkTimelineItem icon="bug_report" color="danger" title="Alert transition bug fixed" date-time="2 days ago" />
|
||||||
|
<MkTimelineItem icon="star" color="warning" title="v0.1.0 tagged" date-time="3 days ago" :last="true" />
|
||||||
|
</MkTimelineList>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ⑪ Counter · Review · Team ───────────────────────────── -->
|
||||||
<section>
|
<section>
|
||||||
<h2 class="text-lg font-bold text-dark mb-0.5">Counter, Review & Team Cards</h2>
|
<h2 class="text-lg font-bold text-dark mb-0.5">Counter, Review & Team Cards</h2>
|
||||||
<p class="text-sm text-secondary mb-4">animated counters · star ratings · horizontal profile</p>
|
<p class="text-sm text-secondary mb-4">animated counters · star ratings · horizontal profile</p>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const rows = [
|
|||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="flex items-center justify-between border-b border-gray-100 px-6 py-4">
|
<div class="flex items-center justify-between border-b border-gray-100 px-6 py-4">
|
||||||
<h6 class="text-sm font-bold text-dark">Authors Table</h6>
|
<h6 class="text-sm font-bold text-dark">Authors Table</h6>
|
||||||
<button class="rounded-lg bg-gradient-primary px-4 py-2 text-xs font-medium text-white shadow-primary hover:shadow-soft-md transition-shadow">
|
<button class="rounded-lg bg-gradient-primary px-4 py-2 text-xs font-medium text-white shadow-primary hover:-translate-y-0.5 active:translate-y-0 transition-all">
|
||||||
+ New Entry
|
+ New Entry
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+24
-2
@@ -1,10 +1,32 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
import tailwindcss from '@tailwindcss/vite'
|
import tailwindcss from '@tailwindcss/vite'
|
||||||
|
import { resolve } from 'path'
|
||||||
|
|
||||||
|
const isLib = process.env.BUILD_TARGET === 'lib'
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
tailwindcss(),
|
// Tailwind only for the app build — consuming projects bring their own
|
||||||
|
!isLib && tailwindcss(),
|
||||||
vue(),
|
vue(),
|
||||||
],
|
].filter(Boolean),
|
||||||
|
|
||||||
|
build: isLib
|
||||||
|
? {
|
||||||
|
lib: {
|
||||||
|
entry: resolve(__dirname, 'src/index.js'),
|
||||||
|
name: 'MkDesignSystem',
|
||||||
|
fileName: format => `mk-design-system.${format}.js`,
|
||||||
|
formats: ['es', 'cjs'],
|
||||||
|
},
|
||||||
|
rollupOptions: {
|
||||||
|
// Vue must be provided by the consuming project
|
||||||
|
external: ['vue'],
|
||||||
|
output: { globals: { vue: 'Vue' } },
|
||||||
|
},
|
||||||
|
// Emit a separate CSS file (design-system.css) consumers can import
|
||||||
|
cssCodeSplit: false,
|
||||||
|
}
|
||||||
|
: {},
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user