Raft 设计系统

基础

视觉舒适度

让一个屏幕整天看着都不累的规则:一条线放在哪里、延伸多远,间距节奏,内缩,对齐,密度,各级文字之间的对比,以及什么时候一块表面才用卡片。

人们整天开着 Raft,所以屏幕必须一眼就能读懂,看上几个小时也不累。舒适来自几种关系,眼睛不用提醒就会去检查:哪些东西是一组,一样东西从哪里开始、到哪里结束,什么最重要。下面每条规则是一句话加上它背后的理由;数值来自间距、对齐、字体排印、颜色和层级,这一页讲的是它们怎么配合。

原则

  • 一条线延伸到它的容器看得见的边界为止。 在有边框的框里,线碰到边框;没有看得见的边时,线缩进到内容的位置(分隔线)。
  • 组内比组间更近。 每个间距都来自间距阶梯(间距节奏)。
  • 离每条边的内缩都一样。 嵌套的内缩会累加(边缘)。
  • 文字在同一条基线上,标记居中;每一栏一条起始边(对齐)。
  • 密度跟着输入方式走,从不跟着内容走。 紧凑列表的一行,触屏时保持至少 44px,鼠标时至少 36px(密度)。
  • 文字层级少,层级之间拉得开。 次要文字是变浅,而不是变小(层级)。
  • 卡片用于放在其他内容里面、或在另一个区域旁边的对象。 不是每一组都要卡片(表面)。

分隔线

线分隔的是一个容器里的同级元素,所以它横跨这个容器看得见的范围。这些规则针对的是紧凑列表:消息下面和 Tasks 卡片里的任务行,以及 Files 卡片里的行(紧凑列表中的行)。其他列表保留它们自己的分隔线(见本节末尾)。

  • 在有边框的框里,线从一边贯穿到另一边,两头都碰到边框。 这样框和它的线组成一组格子。没碰到边框的线两头各留一个空隙,框看起来就像一个带划痕的画框。消息下面的任务就放在这样的框里:它们的线碰到框的边框(任务)。
  • 没有看得见的边时,线的两头都缩进到内容:从文字开始的地方,到最后一个标记结束的地方。 没有可以碰到的东西时,通栏的线读起来像一个区域的边,一条接缝线,把页面切成一条一条。和内容同起同止的线属于内容。较大卡片里的列表,以及侧边卡片在手机上变成的页面,列表周围都没有自己的边,所以它们的线是缩进的:在侧边卡片和手机屏幕里离两边各 16px。
  • 容器是离它最近、有看得见的边、并且只装着这些同级元素的那一个。 只装着这些行的框,就是这些行的容器。还装着标题栏、筛选和分组标题的卡片不是:在那里,列表只是几个部分之一,没有自己的边,它的线和卡片的内容一样缩进。所以 Tasks 卡片的线停在离边 16px 的地方,而消息下面那个框的线碰到它的边框。
  • 分组已经看得出来时,用空白,不用线。 标题、间隔或填充的变化已经把各组分开了,所以标题和它的行之间、第一行上方、最后一行下方,以及两个各有标题的组之间,都不放线。紧挨着边框、接缝线或另一条线的线永远不需要:两条并排的 1px 线读起来是一条 2px 的线(页面结构)。
  • 悬停仍然填满整行。 缩进的线是画在行上的,而不是把行变窄,所以不管线怎样,行的目标区域和它的悬停都从一边贯穿到另一边。

每种线(细线、接缝线、墨色线)的含义见原则。其他列表保留它们现在的分隔线,这条规则不改变它们:InboxItem 列表(动态)和 ListRow 列表的线从文字开始,一直延伸到右边;ListRow 的设置变体,也就是放在内缩卡片里的行,线从离边 16px 的地方开始,而不是从卡片的边框开始。不要把这些分隔线照搬到新的紧凑列表里。

间距节奏

属于一起的东西,彼此之间的距离要比到其他任何东西都近,而且每个间距都取自同一套阶梯。

  • 每个间距都取自间距里的阶梯,包括其中的半级。阶梯之外的数值(5px、14px、18px)说明有东西是在被微调,而不是在被排布。
  • 组与组之间的间距大约是组内间距的两倍,或者更多。 眼睛在读之前就是靠远近来分组的。间隔大小接近时,什么都分不成组,屏幕读起来就是一片均匀的灰。
  • 标题离它所指的内容,要明显比离它前面的内容更近:Tasks 卡片里的状态标题采用 Sidebar 分区标题的间距,文字上方 16px、下方 6px,各组之间不另加间隔,所以它的文字离上面的行(大约 25px)明显比离自己的行(16px)更远。标题离上面的行和离自己的行一样远时,看起来像是两边都属于。标题是一个开关时,让它的触控目标伸进上方的空白,而不是把它推离自己的行。
  • 一行自己的内边距也要算进去。 测量字母和标记之间的距离,而不是框和框之间的距离:一行 36px、文字 14px 时,字母上下已经各有 11px 的空间。

边缘

内容离容器每条边的内缩都一样,这样容器看起来是围着它居中的。

  • 第一个和最后一个看得见的东西缩进同样的距离:左边是第一个字母或开头的标记,右边是最后一个图标框或词(对齐)。Files 卡片里的一个文件行,类型图标从离卡片左边 16px 的地方开始,最后一个操作的图标框在离右边 16px 的地方结束,点击区域向外伸出,所以这一行在卡片里是居中的(紧凑列表中的行)。如果按按钮的框来放,操作结束的地方会比图标开始的地方更靠里,这一行看起来就偏向一边,即使谁也说不出为什么。
  • 较宽的框里的小标记会移动边缘。 一个 32px 的图标按钮按它自己的边来放,它 18px 的图标就在内缩线往里 7px 的地方;一个 44px 的 Back,它 22px 的图标比下面的内容往里 11px。让标记落在内缩线上,框的其余部分向外伸出(标题栏的两端)。
  • 栏末端的无边框按钮按它的文字或图标框对齐,内边距向外伸出:动态筛选右侧的 Mark All Read,文字结束在各行时间结束的那条边距线上(对齐)。
  • 嵌套的内缩会累加。 一个容器放在另一个容器里,从外层的内缩开始,它自己的内容再在此基础上加内层的内缩:消息下面的框从消息的文字栏开始,框里的行再往里 12px 开始。不要用负外边距抵消内缩来凑数字;改容器。唯一的例外是悬挂的内边距:放在纯文字旁边的形状,比如行内 DescriptionList 的值里的一个标签片,按它自己的内边距往外拉,让它的文字从纯文字开始的地方开始,填充向外伸出(纯文字旁边的形状)。
  • 必须有最小宽度的容器,在去掉边距之后仍然保持这个宽度。 至少 280px 的侧边卡片,是在它 12px 的留白以内有 280px,而不是 268px。最小宽度是由卡片必须装下的东西决定的,即一行的标题和它尾部的一组;留白是卡片周围的空间,不是给内容的地方,所以从卡片里扣掉留白,缩小的正是最小宽度要保护的东西。

对齐

一行和一栏里的每个部分落在哪里,由对齐规定,理由也在那里;这一页不另立规则。这些规则最初针对的那些行,按它原样遵循:

密度与触控目标

一个屏幕放多少东西,跟着输入方式走,而不是跟着要显示多少走。

  • 紧凑列表的一行,触屏时至少 44px,鼠标时至少 36px(紧凑列表中的行)。控件的尺寸和它们的点击区域见间距。
  • 空间不够时,截断或换行:永远不要缩小间距或字号。 标题以省略号结尾,尾部一组整体换到第二行。比阶梯更紧的间距让每样东西都更难点中、更难读;想在屏幕上放更多东西的人,可以选更小的消息字号(字体排印)。
  • 一行最多两行文字,只有尾部一组换行时才有第二行。出现第三行,说明这一行装的是一张卡片的内容。

层级对比

一个屏幕上的文字只有几个层级,每一级和下一级都明显分开。

  • 只用少数几种字号和字重。 在桌面端,大多数文字是 14/22,备注是 12/18;列表的一行,标题用一种字号,备注用一种字号。在列表或卡片里,标题靠字重突出,而不是靠自己的字号;只有整块表面的标题,即页面、面板或对话框的标题,才用字号阶梯里的 headline(按用途选字号)。
  • 两个层级之间,字号、字重和颜色最多差两样(较深表面上的备注)。三样同时不同就太吵了。
  • 次要文字用 muted 或 hint,而不是越来越小。 数字、时间、计数和分组标题用 foreground-hint;描述用 foreground-muted(颜色层级)。
  • 一块表面讲的是什么,要排成标题,永远不要排成正文。 在任务详情里,任务的标题就是这块表面的标题,所以它用 headline(text-headline),字重 600,放在说明这是哪个对话框、字重 700 的那条栏下面;排成普通的 16px 文字时,它读起来比栏里的 "Task #1" 还弱。

表面

只有内容本身是一个独立的对象时,才给它卡片或边框。

  • 放在一串其他内容里的对象,值得一张卡片:附件、消息下面的任务、文件卡片。它的边说的是「这是一样东西,和周围的消息分开」。
  • 一个区域放在另一个区域旁边、中间又没有接缝线分开时,值得一张卡片,比如消息列表旁边的侧边卡片。
  • 不要给已经有边的东西再套卡片:页面、面板、对话框,或者内容占满的手机屏幕。在手机上,Tasks 卡片作为一个页面盖住对话:它没有卡片,它的线是缩进的。
  • 不要给每一行一张卡片,也不要嵌套边一样的卡片。 每行一张卡片会把列表变成一摞控件;有边框的框里再套一个有边框的框,是在一条线就够的地方多了一条线。先用空白和标题分组,然后用填充的变化,最后才用线(原则)。
  • 只有浮起的东西才投阴影(层级):布局里的卡片只靠它的边来标出。
  • 间距:阶梯、点击区域和布局宽度。
  • 对齐:什么落在边距线或中线上。
  • 字体排印:字号阶梯和文字层级。
  • 层级:什么会浮起并投阴影。
  • 任务:这些规则最初就是为这种紧凑行写的。