diff --git a/packages/varlet-vue2-ui/src/card/Card.vue b/packages/varlet-vue2-ui/src/card/Card.vue
new file mode 100644
index 0000000..1c54076
--- /dev/null
+++ b/packages/varlet-vue2-ui/src/card/Card.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+ {{ title }}
+
+
+ {{ subtitle }}
+
+
+ {{ description }}
+
+
+
+
+
+
+
+
diff --git a/packages/varlet-vue2-ui/src/card/__tests__/__snapshots__/index.spec.js.snap b/packages/varlet-vue2-ui/src/card/__tests__/__snapshots__/index.spec.js.snap
new file mode 100644
index 0000000..1d0c103
--- /dev/null
+++ b/packages/varlet-vue2-ui/src/card/__tests__/__snapshots__/index.spec.js.snap
@@ -0,0 +1,61 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`test card example 1`] = `
+"
+
基本使用
+
+
+
风景
+
+
公园的树林也很美。在公园的小山上栽满了树木,梧桐树的叶子随着时间的流逝慢慢变黄,纷纷飘落;枫树的叶子却变红了,公园笼罩在片片红云中,也使秋天增添了一分热情。而柏树的叶子仍是那么青翠欲滴,令你陶醉极了。山上有一群孩子在快乐的嬉戏,不时传来阵阵欢笑声,瞧,他们玩得多起劲呀,给树林增添了活力。
+
+
+
显示副标题
+
+
+
风景
+
公园里的风景
+
公园的树林也很美。在公园的小山上栽满了树木,梧桐树的叶子随着时间的流逝慢慢变黄,纷纷飘落;枫树的叶子却变红了,公园笼罩在片片红云中,也使秋天增添了一分热情。而柏树的叶子仍是那么青翠欲滴,令你陶醉极了。山上有一群孩子在快乐的嬉戏,不时传来阵阵欢笑声,瞧,他们玩得多起劲呀,给树林增添了活力。
+
+
+
显示图片
+
![](\\"https://varlet.gitee.io/varlet-ui/cat.jpg\\")
+
风景
+
公园里的风景
+
公园的树林也很美。在公园的小山上栽满了树木,梧桐树的叶子随着时间的流逝慢慢变黄,纷纷飘落;枫树的叶子却变红了,公园笼罩在片片红云中,也使秋天增添了一分热情。而柏树的叶子仍是那么青翠欲滴,令你陶醉极了。山上有一群孩子在快乐的嬉戏,不时传来阵阵欢笑声,瞧,他们玩得多起劲呀,给树林增添了活力。
+
+
+
使用插槽
+
![](\\"https://varlet.gitee.io/varlet-ui/cat.jpg\\")
+
风景
+
公园里的风景
+
公园的树林也很美。在公园的小山上栽满了树木,梧桐树的叶子随着时间的流逝慢慢变黄,纷纷飘落;枫树的叶子却变红了,公园笼罩在片片红云中,也使秋天增添了一分热情。而柏树的叶子仍是那么青翠欲滴,令你陶醉极了。山上有一群孩子在快乐的嬉戏,不时传来阵阵欢笑声,瞧,他们玩得多起劲呀,给树林增添了活力。
+
+
+
水波效果
+
+
+
风景
+
公园里的风景
+
公园的树林也很美。在公园的小山上栽满了树木,梧桐树的叶子随着时间的流逝慢慢变黄,纷纷飘落;枫树的叶子却变红了,公园笼罩在片片红云中,也使秋天增添了一分热情。而柏树的叶子仍是那么青翠欲滴,令你陶醉极了。山上有一群孩子在快乐的嬉戏,不时传来阵阵欢笑声,瞧,他们玩得多起劲呀,给树林增添了活力。
+
+
+
"
+`;
+
+exports[`test card props 1`] = `
+"![\\"This](\\"https://varlet.gitee.io/varlet-ui/cat.jpg\\")
+
This is Card
+
This is subtitle
+
This is description
+
+
"
+`;
diff --git a/packages/varlet-vue2-ui/src/card/__tests__/index.spec.js b/packages/varlet-vue2-ui/src/card/__tests__/index.spec.js
new file mode 100644
index 0000000..1fc3400
--- /dev/null
+++ b/packages/varlet-vue2-ui/src/card/__tests__/index.spec.js
@@ -0,0 +1,66 @@
+import example from '../example'
+import Vue from 'vue'
+import Card from '..'
+import VarCard from '../Card'
+import { mount } from '@vue/test-utils'
+
+test('test card example', () => {
+ const wrapper = mount(example)
+ expect(wrapper.html()).toMatchSnapshot()
+ wrapper.destroy()
+})
+
+test('test card plugin', () => {
+ Vue.use(Card)
+ expect(Vue.component(Card.name)).toBeTruthy()
+})
+
+test('test card props', async () => {
+ const wrapper = mount(VarCard, {
+ propsData: {
+ title: 'This is Card',
+ description: 'This is description',
+ subtitle: 'This is subtitle',
+ src: 'https://varlet.gitee.io/varlet-ui/cat.jpg',
+ fit: 'cover',
+ height: '200px',
+ alt: 'This is an image',
+ elevation: '2',
+ ripple: true,
+ },
+ scopedSlots: {
+ extra: 'text
',
+ },
+ })
+
+ expect(wrapper.find('img').attributes('style')).toMatch('height: 200px')
+ expect(wrapper.find('img').attributes('style')).toMatch('object-fit: cover')
+ expect(wrapper.find('img').attributes('alt')).toMatch('This is an image')
+ expect(wrapper.find('img').attributes('src')).toMatch('https://varlet.gitee.io/varlet-ui/cat.jpg')
+ expect(wrapper.find('.var-card__title').text()).toBe('This is Card')
+ expect(wrapper.find('.var-card__subtitle').text()).toBe('This is subtitle')
+ expect(wrapper.find('.var-card__description').text()).toBe('This is description')
+ expect(wrapper.find('.var-card__footer').text()).toBe('text')
+ expect(wrapper.classes()).toContain('var-elevation--2')
+ expect(wrapper.html()).toMatchSnapshot()
+ wrapper.destroy()
+})
+
+test('test card onClick with null callback', () => {
+ const wrapper = mount(VarCard)
+ wrapper.trigger('click')
+ wrapper.destroy()
+})
+
+test('test card onClick', () => {
+ const onClick = jest.fn()
+ const wrapper = mount(VarCard, {
+ listeners: {
+ click: onClick,
+ },
+ })
+
+ wrapper.trigger('click')
+ expect(onClick).toHaveBeenCalledTimes(1)
+ wrapper.destroy()
+})
diff --git a/packages/varlet-vue2-ui/src/card/card.less b/packages/varlet-vue2-ui/src/card/card.less
new file mode 100644
index 0000000..907e8bc
--- /dev/null
+++ b/packages/varlet-vue2-ui/src/card/card.less
@@ -0,0 +1,85 @@
+@card-padding: 0 0 15px 0;
+@card-background: #fff;
+@card-border-radius: 4px;
+@card-image-width: 100%;
+@card-image-height: 200px;
+@card-title-color: #333;
+@card-title-font-size: 20px;
+@card-title-padding: 0 12px;
+@card-title-margin: 15px 0 0 0;
+@card-subtitle-color: rgba(0, 0, 0, 0.6);
+@card-subtitle-font-size: 14px;
+@card-subtitle-padding: 0 13px;
+@card-subtitle-margin: 10px 0 0 0;
+@card-description-color: rgba(0, 0, 0, 0.6);
+@card-description-font-size: 14px;
+@card-description-margin: 20px 0 0 0;
+@card-description-padding: 0 13px;
+@card-footer-padding: 0 12px;
+@card-footer-margin: 30px 0 0px 0;
+@card-line-height: 22px;
+
+:root {
+ --card-padding: @card-padding;
+ --card-background: @card-background;
+ --card-border-radius: @card-border-radius;
+ --card-image-width: @card-image-width;
+ --card-image-height: @card-image-height;
+ --card-title-color: @card-title-color;
+ --card-title-font-size: @card-title-font-size;
+ --card-title-padding: @card-title-padding;
+ --card-title-margin: @card-title-margin;
+ --card-subtitle-color: @card-subtitle-color;
+ --card-subtitle-font-size: @card-subtitle-font-size;
+ --card-subtitle-padding: @card-subtitle-padding;
+ --card-subtitle-margin: @card-subtitle-margin;
+ --card-description-color: @card-description-color;
+ --card-description-font-size: @card-description-font-size;
+ --card-description-margin: @card-description-margin;
+ --card-description-padding: @card-description-padding;
+ --card-footer-padding: @card-footer-padding;
+ --card-footer-margin: @card-footer-margin;
+ --card-line-height: @card-line-height;
+}
+
+.var-card {
+ border-radius: var(--card-border-radius);
+ overflow: hidden;
+ padding: var(--card-padding);
+ line-height: var(--card-line-height);
+ background: var(--card-background);
+ transition: background-color 0.25s;
+
+ &__image {
+ width: var(--card-image-width);
+ height: var(--card-image-height);
+ display: block;
+ }
+
+ &__title {
+ font-size: var(--card-title-font-size);
+ padding: var(--card-title-padding);
+ margin: var(--card-title-margin);
+ color: var(--card-title-color);
+ }
+
+ &__subtitle {
+ font-size: var(--card-subtitle-font-size);
+ color: var(--card-subtitle-color);
+ padding: var(--card-subtitle-padding);
+ margin: var(--card-subtitle-margin);
+ }
+
+ &__description {
+ font-size: var(--card-description-font-size);
+ color: var(--card-description-color);
+ margin: var(--card-description-margin);
+ padding: var(--card-description-padding);
+ word-break: break-all;
+ }
+
+ &__footer {
+ padding: var(--card-footer-padding);
+ margin: var(--card-footer-margin);
+ }
+}
diff --git a/packages/varlet-vue2-ui/src/card/docs/en-US.md b/packages/varlet-vue2-ui/src/card/docs/en-US.md
new file mode 100644
index 0000000..76ab19e
--- /dev/null
+++ b/packages/varlet-vue2-ui/src/card/docs/en-US.md
@@ -0,0 +1,121 @@
+# Card
+
+### Intro
+
+```js
+import Vue from 'vue'
+import { Card } from '@varlet/ui'
+
+Vue.use(Card)
+```
+
+### Basic Use
+
+```html
+
+```
+
+### Show Subtitle
+
+```html
+
+```
+
+### Show Image
+
+```html
+
+```
+
+### Use Slot
+
+```html
+
+
+ Use Button
+ Use Button
+
+
+```
+
+### Ripple Effect
+
+```html
+
+```
+
+## API
+
+### 属性
+
+| Prop | Description | Type | Default |
+| ------------- | --------------------------------------------------------------- | ------------------ | ------- |
+| `title` | The title of Card | _string_ | `-` |
+| `subtitle` | The subtitle of Card | _string_ | `-` |
+| `description` | The description of Card | _string_ | `-` |
+| `elevation` | The shadow level of Card | _string \| number_ | `2` |
+| `src` | The src of Image | _string_ | `-` |
+| `fit` | Fillmode, opitions `fill` `contain` `cover` `none` `scale-down` | _string_ | `cover` |
+| `alt` | Alt text | _string_ | `-` |
+| `height` | Heigth of Image | _string \| number_ | `-` |
+| `ripple` | Whether to enable ripple | _boolean_ | `false` |
+
+### Slots
+
+| Slot | Description | Arguments |
+| ------------- | --------------------- | --------- |
+| `image` | Custom image | `-` |
+| `title` | Custom title | `-` |
+| `subtitle` | Custom subtitle | `-` |
+| `description` | Custom description | `-` |
+| `extra` | Custom bottom content | `-` |
+
+### Events
+
+| Events | Description | Arguments |
+| ------- | -------------------------------- | -------------- |
+| `click` | Triggered when the Card is click | `event: Event` |
+
+### Style Variables
+
+Here are the CSS variables used by the component, Styles can be customized using [StyleProvider](#/en-US/style-provider)
+
+| Variable | Default |
+| ------------------------------ | -------------------- |
+| `--card-background` | `#fff` |
+| `--card-padding` | `0px 0 15px 0` |
+| `--card-border-radius` | `4px` |
+| `--card-image-width` | `100%` |
+| `--card-image-height` | `200px` |
+| `--card-title-color` | `#333` |
+| `--card-title-font-size` | `20px` |
+| `--card-title-padding` | `0 12px` |
+| `--card-title-margin` | `15px 0 0 0` |
+| `--card-subtitle-color` | `rgba(0, 0, 0, 0.6)` |
+| `--card-subtitle-font-size` | `14px` |
+| `--card-subtitle-padding` | `0 13px` |
+| `--card-subtitle-margin` | `10px 0 0 0` |
+| `--card-description-color` | `rgba(0, 0, 0, 0.6)` |
+| `--card-description-font-size` | `14px` |
+| `--card-description-margin` | `20px 0 0 0` |
+| `--card-description-padding` | `0 13px` |
+| `--card-footer-padding` | `0 12px` |
+| `--card-footer-margin` | `30px 0 0px 0` |
+| `--card-line-height` | `22px` |
diff --git a/packages/varlet-vue2-ui/src/card/docs/zh-CN.md b/packages/varlet-vue2-ui/src/card/docs/zh-CN.md
new file mode 100644
index 0000000..abe3dba
--- /dev/null
+++ b/packages/varlet-vue2-ui/src/card/docs/zh-CN.md
@@ -0,0 +1,126 @@
+# 卡片
+
+### 引入
+
+```js
+import Vue from 'vue'
+import { Card } from '@varlet/ui'
+
+Vue.use(Card)
+```
+
+### 基本使用
+
+```html
+
+```
+
+### 显示副标题
+
+```html
+
+```
+
+### 显示图片
+
+```html
+
+```
+
+### 使用插槽
+
+```html
+
+
+ 添加按钮
+ 添加按钮
+
+
+```
+
+### 水波效果
+
+```html
+
+```
+
+## API
+
+### 属性
+
+| 参数 | 说明 | 类型 | 默认值 |
+| ------------- | --------------------------------------------------------------- | ------------------ | ------- |
+| `title` | 卡片标题 | _string_ | `-` |
+| `subtitle` | 卡片副标题 | _string_ | `-` |
+| `description` | 卡片描述 | _string_ | `-` |
+| `elevation` | 卡片阴影程度等级 | _string \| number_ | `2` |
+| `src` | 图片地址 | _string_ | `-` |
+| `fit` | 填充模式,可选值为 `fill` `contain` `cover` `none` `scale-down` | _string_ | `cover` |
+| `alt` | 替代文本 | _string_ | `-` |
+| `height` | 图片高度 | _string \| number_ | `-` |
+| `ripple` | 是否开启水波 | _boolean_ | `false` |
+
+### 插槽
+
+| 插槽名 | 说明 | 参数 |
+| ------------- | -------------- | ---- |
+| `image` | 自定义图片 | `-` |
+| `title` | 自定义标题 | `-` |
+| `subtitle` | 自定义副标题 | `-` |
+| `description` | 自定义描述 | `-` |
+| `extra` | 自定义底部内容 | `-` |
+
+### 事件
+
+| 事件名 | 说明 | 参数 |
+| ------- | -------------- | -------------- |
+| `click` | 点击卡片时触发 | `event: Event` |
+
+### 样式变量
+
+以下为组件使用的 css 变量,可以使用 [StyleProvider 组件](#/zh-CN/style-provider)进行样式定制
+
+| 变量名 | 默认值 |
+| ------------------------------ | -------------------- |
+| `--card-background` | `#fff` |
+| `--card-padding` | `0px 0 15px 0` |
+| `--card-border-radius` | `4px` |
+| `--card-image-width` | `100%` |
+| `--card-image-height` | `200px` |
+| `--card-title-color` | `#333` |
+| `--card-title-font-size` | `20px` |
+| `--card-title-padding` | `0 12px` |
+| `--card-title-margin` | `15px 0 0 0` |
+| `--card-subtitle-color` | `rgba(0, 0, 0, 0.6)` |
+| `--card-subtitle-font-size` | `14px` |
+| `--card-subtitle-padding` | `0 13px` |
+| `--card-subtitle-margin` | `10px 0 0 0` |
+| `--card-description-color` | `rgba(0, 0, 0, 0.6)` |
+| `--card-description-font-size` | `14px` |
+| `--card-description-margin` | `20px 0 0 0` |
+| `--card-description-padding` | `0 13px` |
+| `--card-footer-padding` | `0 12px` |
+| `--card-footer-margin` | `30px 0 0px 0` |
+| `--card-line-height` | `22px` |
diff --git a/packages/varlet-vue2-ui/src/card/example/index.vue b/packages/varlet-vue2-ui/src/card/example/index.vue
new file mode 100644
index 0000000..211bb91
--- /dev/null
+++ b/packages/varlet-vue2-ui/src/card/example/index.vue
@@ -0,0 +1,74 @@
+
+
+
{{ pack.basicUsage }}
+
+
{{ pack.showSubtitle }}
+
+
{{ pack.showImage }}
+
+
{{ pack.useSlot }}
+
+
+ {{ pack.button }}
+ {{ pack.button }}
+
+
+
{{ pack.showRipple }}
+
+
+
+
+
+
+
diff --git a/packages/varlet-vue2-ui/src/card/example/locale/en-US.ts b/packages/varlet-vue2-ui/src/card/example/locale/en-US.ts
new file mode 100644
index 0000000..977f561
--- /dev/null
+++ b/packages/varlet-vue2-ui/src/card/example/locale/en-US.ts
@@ -0,0 +1,12 @@
+export default {
+ basicUsage: 'Basic Usage',
+ title: 'Little Prince',
+ showSubtitle: 'Show Subtitle',
+ subtitle: 'little prince from',
+ description:
+ 'It took me a long time to learn where he came from. The little prince, whoasked me so many questions, never seemed to hear the ones I asked him. Itwas from words dropped by chance that, little by little, everything wasrevealed to me.',
+ showImage: 'Show Image',
+ useSlot: 'Use Slot',
+ button: 'Use Button',
+ showRipple: 'Ripple Effect',
+}
diff --git a/packages/varlet-vue2-ui/src/card/example/locale/index.ts b/packages/varlet-vue2-ui/src/card/example/locale/index.ts
new file mode 100644
index 0000000..d2e375e
--- /dev/null
+++ b/packages/varlet-vue2-ui/src/card/example/locale/index.ts
@@ -0,0 +1,23 @@
+// lib
+import _zhCN from '../../../locale/zh-CN'
+import _enCN from '../../../locale/en-US'
+// mobile example doc
+import zhCN from './zh-CN'
+import enUS from './en-US'
+import { useLocale, add as _add, use as _use } from '../../../locale'
+
+const { add, use: exampleUse, pack, packs, merge } = useLocale()
+
+const use = (lang: string) => {
+ _use(lang)
+ exampleUse(lang)
+}
+
+export { add, pack, packs, merge, use }
+
+// lib
+_add('zh-CN', _zhCN)
+_add('en-US', _enCN)
+// mobile example doc
+add('zh-CN', zhCN as any)
+add('en-US', enUS as any)
diff --git a/packages/varlet-vue2-ui/src/card/example/locale/zh-CN.ts b/packages/varlet-vue2-ui/src/card/example/locale/zh-CN.ts
new file mode 100644
index 0000000..6af5709
--- /dev/null
+++ b/packages/varlet-vue2-ui/src/card/example/locale/zh-CN.ts
@@ -0,0 +1,12 @@
+export default {
+ basicUsage: '基本使用',
+ title: '风景',
+ showSubtitle: '显示副标题',
+ subtitle: '公园里的风景',
+ description:
+ '公园的树林也很美。在公园的小山上栽满了树木,梧桐树的叶子随着时间的流逝慢慢变黄,纷纷飘落;枫树的叶子却变红了,公园笼罩在片片红云中,也使秋天增添了一分热情。而柏树的叶子仍是那么青翠欲滴,令你陶醉极了。山上有一群孩子在快乐的嬉戏,不时传来阵阵欢笑声,瞧,他们玩得多起劲呀,给树林增添了活力。',
+ showImage: '显示图片',
+ useSlot: '使用插槽',
+ button: '添加按钮',
+ showRipple: '水波效果',
+}
diff --git a/packages/varlet-vue2-ui/src/card/index.ts b/packages/varlet-vue2-ui/src/card/index.ts
new file mode 100644
index 0000000..8548d84
--- /dev/null
+++ b/packages/varlet-vue2-ui/src/card/index.ts
@@ -0,0 +1,10 @@
+import type { VueConstructor } from 'vue'
+import Card from './Card.vue'
+
+Card.install = function (app: VueConstructor) {
+ app.component(Card.name, Card)
+}
+
+export const _CardComponent = Card
+
+export default Card
diff --git a/packages/varlet-vue2-ui/src/card/props.ts b/packages/varlet-vue2-ui/src/card/props.ts
new file mode 100644
index 0000000..3000d05
--- /dev/null
+++ b/packages/varlet-vue2-ui/src/card/props.ts
@@ -0,0 +1,41 @@
+import type { PropType } from 'vue'
+
+function fitValidator(fit: string) {
+ return ['fill', 'contain', 'cover', 'none', 'scale-down'].includes(fit)
+}
+
+export const props = {
+ src: {
+ type: String,
+ },
+ fit: {
+ type: String as PropType<'fill' | 'contain' | 'cover' | 'none' | 'scale-down'>,
+ validator: fitValidator,
+ default: 'cover',
+ },
+ height: {
+ type: [String, Number],
+ },
+ alt: {
+ type: String,
+ },
+ title: {
+ type: String,
+ },
+ subtitle: {
+ type: String,
+ },
+ description: {
+ type: String,
+ },
+ elevation: {
+ type: [Number, String],
+ },
+ ripple: {
+ type: Boolean,
+ default: false,
+ },
+ onClick: {
+ type: Function as PropType<(e: Event) => void>,
+ },
+}
diff --git a/packages/varlet-vue2-ui/types/card.d.ts b/packages/varlet-vue2-ui/types/card.d.ts
new file mode 100644
index 0000000..b944ee7
--- /dev/null
+++ b/packages/varlet-vue2-ui/types/card.d.ts
@@ -0,0 +1,20 @@
+import { VarComponent } from './varComponent'
+
+export interface CardProps {
+ src?: string;
+ fit?: 'fill' | 'contain' | 'cover' | 'none' | 'scale-down';
+ height?: string | number;
+ alt?: string;
+ title?: string;
+ subtitle?: string;
+ description?: string;
+ elevation?: string | number;
+ ripple?: boolean;
+ onClick?: (e: Event) => void;
+}
+
+export class Card extends VarComponent {
+ $props: CardProps
+}
+
+export class _CardComponent extends Card {}
diff --git a/packages/varlet-vue2-ui/types/global.d.ts b/packages/varlet-vue2-ui/types/global.d.ts
index 4279dba..8912eec 100644
--- a/packages/varlet-vue2-ui/types/global.d.ts
+++ b/packages/varlet-vue2-ui/types/global.d.ts
@@ -2,6 +2,8 @@ declare module 'vue' {
export interface GlobalComponents {
VarButton: typeof import('@varlet-vue2/ui')['_ButtonComponent']
VarLazy: typeof import('@varlet-vue2/ui')['_LazyComponent']
+ VarCell: typeof import('@varlet-vue2/ui')['_CellComponent']
+ VarCard: typeof import('@varlet-vue2/ui')['_CardComponent']
VarLocale: typeof import('@varlet-vue2/ui')['_LocaleComponent']
VarSkeleton: typeof import('@varlet-vue2/ui')['_SkeletonComponent']
VarChip: typeof import('@varlet-vue2/ui')['_ChipComponent']
diff --git a/packages/varlet-vue2-ui/types/index.d.ts b/packages/varlet-vue2-ui/types/index.d.ts
index 92c1d18..2541b3b 100644
--- a/packages/varlet-vue2-ui/types/index.d.ts
+++ b/packages/varlet-vue2-ui/types/index.d.ts
@@ -3,6 +3,8 @@ import type { VueConstructor } from 'vue'
export const install: (app: VueConstructor) => void
export * from './button'
+export * from './card'
+export * from './cell'
export * from './locale'
export * from './skeleton'
export * from './cell'