この記事は Goodpatch Advent Calendar 2020 の15日目です。6日目の記事 Abstract API を使った CI の構成を考える に関連し、Sketch Assistants を DesignOps に着目して紹介します。
なお、この記事の内容は Sketch Version 70.2 で動作確認しています。
DesignOps
はじめに「DesignOps」の定義を確認しましょう。DesignOpsの基礎 – U-Site では、次のように定義されています。
定義:DesignOpsとは、スケールしたときのデザインの価値と影響を増大させるために、人材とプロセス、プロダクトを組織化して、最適化することである。
DesignOpsは、以下のような課題に対する取り組みの総称である:
- デザインチームの成長と進化
- 適切なスキルをもつ人材の発掘と採用
- 効率的なワークフローの作成
- デザインアウトプットの品質と影響の改善
「DesignOps」には、私たちが普段慣れ親しんでいる「DevOps 1 」的なアプローチが背景にあります。上記の「効率的なワークフローの作成」という課題については、DevOps のプラクティスの「継続的インテグレーション(CI)」を適用できそうです。
UI デザインツールとして広く利用されている Sketch には Assistants という仕組み(機能)が提供されています。Assistants を使うことで、いわゆる Linter として Sketch ファイルを静的解析できます。後述する sketch-assistant-cli も提供されており、CI による静的解析の自動化も実現可能だと考えています。
Sketch Assistants
Sketch Assistants は、Sketch — Introducing Assistants — a helping hand for your designs で紹介された Sketch の機能の一つで、次の特徴があります。
- ドキュメントの問題点を発見し、デザインシステムとの整合性を保ち、共同作業のためのファイルを準備するのに役立つ
- Sketch Mac app の汎用拡張機能である「プラグイン」とは異なり、Sketch内でも、どのプラットフォームでもコマンドラインでも動作する
- 既存の Assistants を修正するか、TypeScript で独自のルール実装を書くことで、独自の Assistants を作成できる
Sketch Assistants には、Naming Conventions や Tidy などの Assistant が用意されています。これらを Sketch に導入することで「空の Group 」「非表示の Layer 」など、人力では見つけにくい箇所を、機械的に発見できるようになります。既存の Assistants は Sketch — Assistants (extensions) で探せます。
Sketch Assistant を使う
Assistants の使い方を詳しく知りたい場合は、Sketch — Assistants (docs) を参照してください。
Toolbar の[Assistants]をクリックすると、次のウインドウが表示されます。
[Find Assistants]をクリックすると、https://www.sketch.com/extensions/assistants/ が表示されます。
今回は Sketch — Tidyをインストールします。インストールするには[Add to Sketch]をクリックします。
Tidyがインストールされると、Tidyの実行結果が Assistants ウインドウに表示されます。警告された項目をクリックすると、該当箇所が表示されます。
Sketch Assistant を作る
公式ドキュメント Sketch Developer — Getting started です。
自作 Assistant のパターンとしては大きつ二つあります。
- Sketch が提供している Core Rules を参照し、ルールのオプションを指定して、ルールの条件を調整する(普段使用している Linter のルールを適用する感覚と同じです)
- 自作のロジックを実装する
開発の流れ
開発環境として Node.js version 12+ が必要になります。GitHub に Sketch Assistantテンプレートのリポジトリ が用意されていますので、このテンプレートを使って開発を始めます。
-
npm install
でパッケージをインストール - ルールとそのテストを実装
- テストで参照する Sketch ファイルを作成
-
npm run test
でテストを実行 -
npm run package-tarball
でビルドして、パッケージ化する(tgzファイルが生成される) - tgz ファイルを Sketch Assistants にインストールして動作確認
注意点
- ルール名は Assistant のエコシステムで一意の識別子となるため、ルール名の前には親の Assistant名を
/
で区切って付けます- 例:
my-sketch-assistant/text-disallow
- 例:
- できるだけ設定可能なルールを実装しましょう
- 例えば「lorem ipsum」文字列を探すルールの場合、「lorem ipsum」をハードコードするのではなく、オプションとして受け取るようにしましょう。一般化することで、設定が可能になり、公開された後のAssistant は Sketchユーザーにとってより広く有用なものになります。
- オプションは
utils.getOption
を使用して参照できます
- デフォルトでは、ルールがアクティブではありません
- Assistant の config オブジェクトで明示的に有効にする必要があります
自作のロジックを実装するパターン
テキストレイヤーの文字列に「lorem-ipsum」を含む場合に警告するルールを実装してみます。なお、このルールのプロジェクトは mnkd/first-sketch-assistantで公開しています。
最初に Sketch Assistant Templateを基にしてrepoを用意して、実装を進めます。以下は、index.ts
の抜粋です。
-
utils.getOption()
で optionpattern
を取得しています -
for (const text of utils.objects.text) {}
で、ドキュメント内のすべてのテキストレイヤーに対して、評価しています-
text
の他page
artboard
group
color
などの情報を取得できます
-
- 評価した結果
pattern
を含む場合、utils.report()
でレポートします
import { AssistantPackage, RuleDefinition } from '@sketch-hq/sketch-assistant-types'
const textDisallow: RuleDefinition = {
rule: async (context) => {
const { utils } = context
const pattern = utils.getOption('pattern')
if (typeof pattern !== 'string') throw Error()
for (const layer of utils.objects.text) {
const value = layer.attributedString.string
if (value.includes(pattern)) {
utils.report(`Layer "${layer.name}" contains "${pattern}"`, layer)
}
}
},
name: 'first-sketch-assistant/text-disallow',
title: (config) => `Text should not contain "${config.pattern}"`,
description: 'Reports a violation when text layers contain a configurable text pattern',
}
const assistant: AssistantPackage = async () => {
return {
name: 'first-sketch-assistant',
rules: [textDisallow],
config: {
rules: {
'first-sketch-assistant/text-disallow': {
active: true,
pattern: 'lorem-ipsum',
},
},
},
}
}
パッケージ化
作成したルールは npm run package-tarball
でビルドしてパッケージ化します。パッケージ化に成功すると、tgzファイルが生成されます。
tgzファイルは[Assistants]ウインドウの[Manage Assistants…]からインストールできます。
[Manage Assistants...]をクリックすると、次の画面になりますので、さらに[Add from Archive…]をクリックしてください。クリックするとファイルを選択するウインドウが表示されますので、tgzファイルを選んでください。
tgzファイルを選択すると、Assistant が実行され、次のように表示されます。
テスト駆動の Assistant 開発
Sketch Developer — Run and test Assistants で「Test-driven Assistant development」と記載されているとおり、テストしやすく整備されています。また、Core Rules の各ルールのコードからテストの実装方法を確認できます。
最初に、各テストで参照する Sketch ファイルを用意します。ベストプラクティスとしては、テスト単位で Sketch ファイルを用意しておくとよいでしょう。
例えば、first-sketch-assistant/text-font-name
をテストしたい場合は、次のようにテストコードを記述します。
import { resolve } from 'path'
import { testAssistant } from '@sketch-hq/sketch-assistant-utils'
import Assistant from '..'
test('text-font-name', async () => {
const { violations, ruleErrors } = await testAssistant(
resolve(__dirname, './font-name.sketch'),
Assistant,
)
const textFontNameViolations = violations.filter(
(v) => v.ruleName === 'first-sketch-assistant/text-font-name'
)
expect(textFontNameViolations).toHaveLength(1)
expect(ruleErrors).toHaveLength(0)
})
次にテストを実行して、期待どおりの結果が得られるかチェックします。
$ npm run test
> first-sketch-assistant@1.0.0 test /path/to/first-sketch-assistant
> jest --no-cache
PASS src/__tests__/index.test.ts
✓ text-font-name (22 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3.037 s
Ran all test suites.
Core Rules を活用するパターン
Core Rules は sketch-assistants/assistants/core にリストアップされています。この中から、適用したいルールを選択します。
ルールを実装するには、はじめに sketch-hq/sketch-core-assistant
を追加します。
$ npm install -S @sketch-hq/sketch-core-assistant
$ npm run package-tarball
した際に、次のようなエラーが発生する場合は、package.json
の dependencies
の @sketch-hq/sketch-assistant-utils
や @sketch-hq/sketch-assistant-types
@sketch-hq/sketch-core-assistant
のバージョンを揃えるなど、対応してみてください。
node_modules/@sketch-hq/sketch-file-format-ts/dist/cjs/v3-types.d.ts:477:5
477 changeIdentifier: number;
例えば、次のルールを適用したい場合は、下記 index.ts
のように記述します。
-
layers-no-hidden
- レイヤーリストで視覚的に非表示になっているレイヤーは違反とみなされます
- groups-no-redundant
-
layers-no-loose
- アートボードの外側に存在するレイヤーは違反とみなされます
-
name-pattern-artboards
- Artboard 名に「Copy」を含む場合に指摘する
-
forbidden
option に正規表現で指摘できる
import CoreAssistant from '@sketch-hq/sketch-core-assistant'
import { AssistantPackage } from '@sketch-hq/sketch-assistant-types'
const assistant: AssistantPackage = [
CoreAssistant,
async () => {
return {
name: 'my-rules',
rules: [],
config: {
rules: {
'@sketch-hq/sketch-core-assistant/layers-no-hidden': {
active: true,
},
'@sketch-hq/sketch-core-assistant/groups-no-redundant': {
active: true,
},
'@sketch-hq/sketch-core-assistant/layers-no-loose': {
active: true,
},
'@sketch-hq/sketch-core-assistant/name-pattern-artboards': {
active: true,
"allowed": [],
"forbidden": ['Copy'],
},
},
},
}
},
]
export default assistant
Sketch Assistant を CLI から使う
Sketch は sketch-assistant-cli も提供しています。CLI を使うことで Mac以外の環境でも様々な Assistant を使ったワークフローが可能になり、CI の実現も容易になります。
例えば、先ほどの「Core Rules を活用するパターン」と、同等のルールを定義してみます。
{
"name": "my-rules",
"dependencies": {
"@sketch-hq/sketch-core-assistant": "latest"
},
"assistant": {
"extends": [
"@sketch-hq/sketch-core-assistant"
],
"config": {
"rules": {
"@sketch-hq/sketch-core-assistant/layers-no-hidden": {
"active": true
},
"@sketch-hq/sketch-core-assistant/groups-no-redundant": {
"active": true
},
"@sketch-hq/sketch-core-assistant/layers-no-loose": {
"active": true
},
"@sketch-hq/sketch-core-assistant/name-pattern-artboards": {
"active": true,
"allowed": [],
"forbidden": ["Copy"]
}
}
}
}
}
--assistant
に、上記の JSON を指定して sketch-assistants
を実行すると、次のような実行結果が得られます。
$ sketch-assistants --assistant=assistant.json iOS_02.sketch
✔ iOS_02.sketch: Ready
File: /path/to/iOS_02.sketch
custom/my-rules
Grade: fail
Violations: 10
Rule errors: 0
Groups should not be redundant
Detail: This group is redundant
Severity: error
Rule: @sketch-hq/sketch-core-assistant/groups-no-redundant
Object: Name : Top | Pointer : /document/pages/0/layers/0/layers/1 | Id : 85F36E12-9801-40A5-842A-B97B7AFE2CE2 | Class : 85F36E12-9801-40A5-842A-B97B7AFE2CE2
Groups should not be redundant
Detail: This group is redundant
Severity: error
Rule: @sketch-hq/sketch-core-assistant/groups-no-redundant
Object: Name : lv1/M | Pointer : /document/pages/0/layers/0/layers/4/layers/1/layers/0/layers/1 | Id : 754EA6E0-5B96-4BF3-B5AA-B2485BB24312 | Class : 754EA6E0-5B96-4BF3-B5AA-B2485BB24312
..snip..
--json
を指定することで、JSONとして実行結果を得られます。
$ sketch-assistants --assistant=assistant.json iOS_02.sketch --json
JSON であれば、得意のプログラミング言語を使ってパースした結果を基にして、次のアクション(Slackへ通知など)へ移せそうです。
まとめ
- Design の世界にも「DevOps」に似た「DesignOps」という考え方がある
- 「Sketch Assistant」を使って、Sketchファイルを静的解析できる(Linterを作成できる)
- 「Sketch Assistant CLI」を使うことで CLI を使って Assistant を実行できる
ということが分かりました。
Next Action
Abstract API を使った CI の構成を考える で言及されている「CI による自動化」のパーツが揃いつつあります。Abstract が提供する Webhooksが使えるようになると、例えば commits.created
のイベントをフックにして、Sketch Assistant CLI で静的解析し、その結果を Slack などへ通知し、Sketch ファイルの修正・改善をUIデザイナーに促す、というフローを構築できそうです。構築したあとは、そのフローが有効かどうか評価し、その結果を共有できればと考えています。
最後に
現時点では道半ばでありますが、エンジニアとして「DevOps」で培った知見を「DesignOps」に活かすことで、UIデザイナーがデザインに集中できるようアシストし、質の高いデザインを促進する活動に貢献できればと考えています。
明日の Goodpatch Advent Calendar 2020 は @kazu_death がお送りします。
参考
- Abstract API を使った CI の構成を考える
- Sketch — The digital design toolkit
- Sketch Developer — Assistants
- DesignOpsの基礎 – U-Site
- DesignOps Handbook - DesignBetter
- DesignOps: From discovery to successful scaling
-
DevOps については DevOps とは? - DevOps と AWS や 「DevOps」を全く知らない人のために「DevOps」の魅力を伝えるための「DevOps」入門 - Qiita などを参照してください ↩