👶

Hackableなスライド作成ツールSlidevで遊ぶ

2022/01/15に公開

2022年1月15日、エンジニアと人生コミュニティのLT会で「Hackableなスライド作成ツールSlidevで遊ぶ」というスライドを発表してきました。
今回はそのスライドの記事版です。
スライドに書けなかったこともあったので、こちらの記事のほうがわかりやすくなっていると思います。

アーカイブはこちら

発表したスライドをホスティングしたもの
https://inspiring-allen-a98eff.netlify.app/1

GitHub
https://github.com/hiroko-ino/slidev-lt

Slidevとは?

Presentation Slides for Developersとあるように、開発者向けのスライド作成ツールです。

https://sli.dev/

公式サイトのドメインの使い方がシャレオツですね。

以下の機能で構成されています。

  • 📝 Text-based - マークダウンを使用し、スタイルを設定出来ます
  • 🎨 Themable - npm packageを使用しテーマをシェア出来ます
  • 🧑‍💻 Developer Friendly - コードハイライト, オートコンプリートを使用したライブコーディング
  • 🤹 Interactive - Vueコンポーネントの埋め込みで表現を強化します
  • 🎥 Recording - ビルトインの録画とカメラビューがあります
  • 📤 Portable - PDFへのエクスポート, PNG, ホスティング可能なSPA
  • 🛠 Hackable - Webページで可能なことは何でも

ベータ版でありますし、まだAPI等変更の可能性があるようです。

Slidev is still under heavy development. API and usages are not set in stone yet.

おすすめポイント

  • マークダウン内にhtmlを書いていけばかなりの自由度
  • Windi CSS(Tailwind CSS 2.0完全互換の動的生成コンパイラ)が内蔵されている https://windicss.org/
  • ページごとに設定出来るScopedなstyle
  • スライドをgit管理出来る
  • 簡単にホスティング(Netlify, vercel, GitHub Pages)
  • 『Webページで可能なことは何でも』の言葉とおり、VueやCSSの知識で好きなようにカスタマイズ出来る

Webページで可能なことは何でもというのがSlidevをまさしく体現する言葉だと思います。
今回はそのHackableな部分もカスタマイズで堪能していきます。

始め方

With NPM:
$ npm init slidev
With Yarn:
$ yarn create slidev

で一発で始められます。
こちらのコマンドを打つと、プロジェクトフォルダ名とnpmパッケージのインストール方法を聞かれた後、下記のようなプロジェクトフォルダが作成されます。

components/ => このフォルダ内のVueファイルがコンポーネントとして扱える
.gitignore
.npmrc
.netlify.toml => Netlifyホスティング用
package.json
README.md
slides.md => このファイルが単一のエントリーポイントとなり、プロジェクト内で一つのスライドを生成する
vercel.json => Vercelホスティング用
yarn.lock

今の所コマンドで作成したプロジェクトフォルダにつき1つのスライドという形のようです。

マークダウンの記法

ハイフン3つで区切られた箇所が一つのスライドとして扱われます。

# Slidev

Hello, World!

---

# Page 2

Directly use code blocks for highlighting

このコードブロックを閉じてしまうので書けませんでしたが、
バッククオート3つで囲えばハイライトされたコードブロックを作ることができます。

---

# Page 3

You can directly use Windi CSS and Vue components to style and enrich your slides.
VueコンポーネントとWindi CSSを直接扱い、スライドをスタイル・エンリッチすることが出来ます。

<div class="p-3">
  <Tweet id="20" />
</div>

ディレクトリ構造

デフォルトのプロジェクトに入っているファイル以外にも、作成することでカスタマイズに使用できるものがあります。

https://sli.dev/custom/directory-structure.html

your-slidev/
  ├── components/       # カスタムコンポーネント
  ├── layouts/          # カスタムレイアウト
  ├── public/           # 静的アセット
  ├── setup/            # カスタム setup / hooks
  ├── styles/           # カスタム style
  ├── index.html        # index.htmlへ注入する
  ├── slides.md         # エントリーポイント
  └── vite.config.ts   # vite config

components
Vueのコンポーネントを置くことでslides.mdで使用できるようになります。

layouts
slides.mdファイル内では、layout: image-rightなどとすることによりレイアウトを変更出来ます。こちらの設定を上書きする・新規作成することが出来ます。

public
静的アセット置く場所です。画像や独自のjsファイルなど。

styles
グローバルなCSSファイルを置くことが出来ます。

// styles/index.ts

import './base.css'
import './code.css'
import './layouts.css'

index.html
index.htmlにマージされる記述をすることが出来ます。

<!-- ./index.html -->
<head>
  <link rel="preconnect" href="https://fonts.gstatic.com">
  <link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;600&family=Nunito+Sans:wght@200;400;600&display=swap" rel="stylesheet">
</head>

<body>
  <script src="./your-scripts"></script>
</body>

こちらが下のようにホスティングされます。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/slidevjs/slidev/assets/favicon.png">
  <!-- injected head -->
  <link rel="preconnect" href="https://fonts.gstatic.com">
  <link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;600&family=Nunito+Sans:wght@200;400;600&display=swap" rel="stylesheet">
</head>
<body>
  <div id="app"></div>
  <script type="module" src="__ENTRY__"></script>
  <!-- injected body -->
  <script src="./your-scripts"></script>
</body>
</html>

グローバルレイヤー
global-top.vue | global-bottom.vue というファイルを作成するとスライド共通のコンポーネントとして扱う事ができます。

フォントを扱う

日本語でスライドを作成するならまず日本語フォントを当てたいですよね。
fontsという設定をslides.md内の設定ブロック内に書けば適用されます。

fonts:
  # basically the text
  sans: 'Robot'
  # use with `font-serif` css class from windicss
  serif: 'Robot Slab'
  # for code blocks, inline code, etc.
  mono: 'Fira Code

Googleフォントは、自動でインポートされるのでfontsにsans: 'Noto Sans JP'などと記述するだけでOK!

Fonts will be imported automatically from Google Fonts. That means you can use any fonts available on Google Fonts directly.

今の所Webフォントの自動インポートはGoogleフォントのみの対応となっているようです。

変数を扱う

$slidev.nav.currentPage(現在のページ番号)など、用意されている変数はいくつかあり、GitHubのソースコードを見ると下記に紹介されている以上にもっとあります。
https://sli.dev/custom/vue-context.html

このあたりに色々ある
https://github.com/slidevjs/slidev/blob/main/packages/client/logic/nav.ts

ただ、独自の変数を使う方法は、ドキュメント内を"variable"で検索したけどなさそう?
(繰り返し使うテキストを変数にいれておくなど…)

CSSに変数を使いたいなら./style.cssもしくは./styles/xx.css(グローバルのCSSファイル)を作成し、CSS カスタムプロパティでよさそう

// style.css
:root {
  --main-color: #FB98B7;
  --main-color-sheer: #FFC0D1;
  --main-color-vivit: #FF48A5;
  --main-color-red: #FF005F;
  --main-color-blue: #4DCCD2;
  --main-color-black: #333;
}

アニメーションを扱う

Slidevには@vueuse/motion(https://motion.vueuse.org/)が内蔵されています。
こちらを用いてスライドに達した瞬間などにアニメーションをすることが出来ます。

↓共通化したかったのでコンポーネント化した例です。別にコンポーネント化せず直接slides.mdで使用していいと思います。

<template>
    <div 
        v-if="isShow" // $slidev.nav.currentPage === スライドのindex
        v-motion
        :initial="{
            y: 50,
        }"
        :enter="{
            y: 0,
        }">
        <slot />
    </div>
</template>

// slides.md
<Fadein :currentPage="$slidev.nav.currentPage" :index="10"> // ここに適した変数を探したのですがタイムアップしました…
マークダウンなどを書く
</Fadein>

今回の特殊なカスタマイズ

カメラのCSSを調整

カメラのVideoタグのCSSをいじりたかったのですが、カメラ付近のDOMはこうなっています。

<!--v-if-->
<div id="slidev-goto-dialog" class="fixed right-5 bg-main transform transition-all -top-20" shadow="~" p="x-4 y-2" border="~ transparent rounded dark:gray-400 dark:opacity-25" data-v-7df26b02="">
  <input type="number" disabled="" class="outline-none bg-transparent" placeholder="Goto..." data-v-7df26b02="">
</div>
<div class="fixed z-10" style="left: 838.861px; top: 432.301px;">
  <div class="rounded-full shadow bg-gray-400 bg-opacity-10 overflow-hidden object-cover" style="width: 177px; height: 177px;">
    <video autoplay="" class="object-cover min-w-full min-h-full rounded-full" style="transform: rotateY(180deg);"></video>
  </div>
  <div class="absolute bottom-0 right-0 rounded-full bg-main shadow opacity-0 shadow z-30 hover:opacity-100 dark:border dark:border-true-gray-700" style="width: 14px; height: 14px; top: 144.087px; left: 144.087px; cursor: nwse-resize;">
  </div>
</div>
<!--v-if-->

Windi CSSのクラスだけが並んでいる…🤔(カメラの部分はfixed z-10の箇所)
となったのですが手前にslidev-goto-dialogがありましたので隣接セレクタでなんとかなります。

// style.css#slidev-goto-dialog + div > div {
  なにかする
}

この装飾はICSさんの記事を参考にしました。
(本当はみなさんにカメラつけて見ていただきたいのですが、ホスティングするとカメラ使えないっぽい?)

https://ics.media/entry/15130/

カメラのvideoを正方形にしたりとか角丸にしたりとかも可能です。

独自のプログレスバーを作る

スライドの下についているページの現在地を表すバーです😁

global-bottom.vueというのをルートに作成するとするとスライド共通コンポーネントが置けます。そちらに変数を渡してcomputedで計算しています。

// global-bottom.vue
<template>
  <Progress
    v-if="$slidev.nav.currentPage !== 1 && $slidev.nav.currentLayout!== 'end'"
    :current="$slidev.nav.currentPage"
    :total="$slidev.nav.total" />
</template>
// components/Progress.vue
<template>
    <div class="absolute z-50 bottom-5 left-0 right-0 flex justify-center">
        <div class="w-50 relative h-1 bg-green-500/50 rounded-full overflow-hidden">
            <span class="absolute h-full bg-gray-500 transition-all duration-300" :style="style"></span>
        </div>
    </div>
</template>

<script lang="ts">
  export default {
    name: "Progress",
    // propsを受け取る
    computed: {
      style() {
        return {
          width: `${100 / (this.total - 1)}%`,
          left: `${100 / (this.total - 1) * (this.current - 2)}%`,
        }
      }
    }
  };

このプログレスバーが需要あるかどうかはわかりませんが、Slidevで遊び倒せる証拠にはなっていると思います。

endを変更する

最後のスライドはslides.mdでは管理できない…🤔自動的についてきます。

デフォルトのendが味気ない!

実はlayoutsの中にendがあります。
layoutsフォルダ内に同名コンポーネントを作成すると上書き出来るので、layouts/end.vueで上書き出来ます。

<template>
  <div class="slidev-layout default">
    // なにかする
  </div>
</template>

ローカル > テーマ > ビルトインの順番の優先度で上書きされていきます。

If the layout you provide has the same name as a built-in layout or a theme layout, your custom layout will take precedence over the built-in/theme layout. The priority order is local > theme > built-in.

マークダウンだからこその副産物!

この記事を書いていて気づきましたが、Slidevのスライドはマークダウンなので、Zennなどの記事投稿へのコピペもめちゃくちゃ捗ります!!!👌
正直これが最強のおすすめポイントかもしれません。

総括

Slidevは『Hackable』な名に恥じない開発者にとってはかゆいところに手が届く
素晴らしいスライド作成ツールでした!

ベータ版なので今後にも期待!

Discussion