Open4
Vite + Vue + UnoCSS 環境構築

予定
今後同タイトル(テーマ)の記事を書く予定。

手順
まずはべた書き
node
とかのバージョンを確認する
Vue プロジェクトを作成
pnpm create vue@latest
ウィザードへの回答
Project name: try-vite-vue-unocss
Add TypeScript? > Yes
Add JSX Support? > No
Add Vue Router for Single Page Application development? > No
Add Pinia for state management? > No
Add Vitest for Unit Testing? > No
Add an End-to-End Testing Solution? > No
Add ESLint for code quality? > Yes
Add Prettier for code formatting? > Yes
Add Vue DevTools 7 extension for debugging? (experimental) > No
prettier
の設定ファイル
テキトー。
.prettierc.json
{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": true,
"tabWidth": 2,
"singleQuote": false,
"printWidth": 80,
"trailingComma": "es5"
}
Vue プロジェクトの作成成功を確認
cd try-vite-vue-unocss
pnpm install
pnpm format
pnpm dev
localhost:5173 を確認して、サーバを停止しておく。
UnoCSS を試す
VS Code 拡張機能
- UnoCSS
- Iconify
UnoCSS のインストール
pnpm add -D unocss
設定ファイルの修正
uno.config.ts
import { defineConfig } from "unocss";
export default defineConfig({
rules: [
["m-100px", { margin: "100px" }],
["text-green", { color: "green" }],
],
});
vite.config.ts
import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import UnoCSS from "unocss/vite";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), UnoCSS()],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
});
src/main.ts
import { createApp } from "vue";
import App from "./App.vue";
import "uno.css";
createApp(App).mount("#app");
不要なものを消す
src/assets/
src/components/
rm -rf src/assets/ && rm -rf src/components/
ユーティリティを使い始める
App.vue
<template>
<div class="m-100px text-green">Hello World!</div>
</template>
サーバを起動して、ユーティリティが有効かどうかを確認する。
pnpm dev
Attributify をためす
設定ファイルの修正 & ユーティリティの追加
uno.config.ts
import { defineConfig, presetAttributify } from "unocss";
export default defineConfig({
presets: [presetAttributify()],
rules: [
["m-100px", { margin: "100px" }],
["text-green", { color: "green" }],
["text-bold", { "font-weight": "700" }],
],
});
src/App.vue
<template>
<div m-1 text="green bold">Hello World!</div>
</template>
グリッドレイアウトを UnoCSS で実装
リセットCSS をインストール
pnpm add @unocss/reset
src/main.ts
import { createApp } from "vue";
import App from "./App.vue";
import "@unocss/reset/tailwind.css";
import "uno.css";
createApp(App).mount("#app");
presetTagify
, presetIcons
の追加
uno.config.ts
import {
defineConfig,
presetAttributify,
presetTagify,
presetIcons,
presetUno,
} from "unocss";
export default defineConfig({
presets: [
presetAttributify(),
presetTagify(),
presetIcons({
scale: 2.0,
autoInstall: true,
}),
presetUno(),
],
});
index.html
の修正
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app" h-100vh></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
App.vue
を修正してグリッドレイアウトを実装
App.vue
<template>
<div
p-5
grid
gap="x-8 y-8"
grid-cols="sm:2 md:3 lg:4"
h-full
border="[&>div]:~ [&>div]:solid"
class="[&>div]:rounded-lg [&>div]:grid [&>div]:justify-center [&>div]:items-center [&>div>*]:text-4xl"
>
<div border-red><i-carbon-logo-github text-green /></div>
<div border-orange><i-carbon-logo-vue text-green /></div>
<div border-yellow><i-twemoji-frog /></div>
<div border-green><i-twemoji-green-heart /></div>
<div border-gray><i-logos-unocss /></div>
<div border-blue><i-devicon-nuxtjs /></div>
<div border="#000080"><i-devicon-homebrew /></div>
<div border-purple><i-logos-vitejs /></div>
<div border-pink><i-logos-visual-studio-code /></div>
</div>
</template>

手順
Vue らしくコンポーネントに切り出したい
GridLayout.vue
src/components/GridLayout.vue
<script setup lang="ts">
const props = defineProps<{
gap: string;
columnsLg: string;
columnsMd: string;
columnsSm: string;
}>();
</script>
<template>
<div
grid
px-5
pb-5
:gap="props.gap"
:class="[columnsLg, columnsMd, columnsSm]"
>
<slot />
</div>
</template>
src/components/GridIcon.vue
<script setup lang="ts">
import { computed } from "vue";
const props = defineProps<{
borderColor?: string;
icon?: string;
isDark: boolean;
}>();
const borderClass = computed(() => {
return props.borderColor || "border-gray";
});
const iconClass = computed(() => {
return props.icon || "i-logos-vue";
});
</script>
<template>
<div
grid
items-center
justify-center
border
rounded-lg
border-solid
:class="borderClass"
hover="bg-gray"
>
<span
text-4xl
:class="[iconClass, { 'text-white': isDark }]"
>
</span>
</div>
</template>
navbar を作成して、ダークモード風を実装
src/components/NavBar.vue
<script setup lang="ts">
import { computed } from "vue";
const props = defineProps<{
isDark: boolean;
}>();
const emit = defineEmits<{
(event: "toggleDarkMode"): void;
}>();
const emitToggleDarkMode = () => {
emit("toggleDarkMode");
};
const iconClass = computed(() => {
return props.isDark ? "i-mi-moon text-white" : "i-mi-sun";
});
</script>
<template>
<nav
h-10vh
w-full
px-10
flex
justify-end
items-center
>
<button @click="emitToggleDarkMode">
<span
text-2xl
flex
content-center
:class="iconClass"
></span>
</button>
</nav>
</template>
src/App.vue
<script setup lang="ts">
import { ref } from "vue";
import GridLayout from "./components/GridLayout.vue";
import GridIcon from "./components/GridIcon.vue";
import Navbar from "./components/Navbar.vue";
const isDark = ref(false);
const toggleDarkMode = () => {
isDark.value = !isDark.value;
};
const items = [
{ borderColor: "border-red", icon: "i-carbon-logo-github" },
{ borderColor: "border-orange", icon: "i-carbon-logo-vue" },
{ borderColor: "border-yellow", icon: "i-twemoji-frog" },
{ borderColor: "border-green", icon: "i-twemoji-avocado" },
{ borderColor: "border-gray", icon: "i-logos-unocss" },
{ borderColor: "border-blue", icon: "i-devicon-nuxtjs" },
{ borderColor: "border-#000080", icon: "i-devicon-homebrew" },
{ borderColor: "border-purple", icon: "i-logos-vitejs" },
{ borderColor: "border-pink", icon: "i-logos-visual-studio-code" },
];
</script>
<template>
<div :class="{ 'bg-black': isDark }">
<Navbar
:isDark
@toggleDarkMode="toggleDarkMode"
>
</Navbar>
<GridLayout
:gap="'x-8 y-8'"
:columnsLg="'lg:grid-cols-4'"
:columnsMd="'md:grid-cols-3'"
:columnsSm="'sm:grid-cols-2'"
h-90vh
>
<GridIcon
v-for="(item, index) in items"
:key="`icon-${index}`"
:borderColor="item.borderColor"
:icon="item.icon"
:isDark
/>
</GridLayout>
</div>
</template>