CSS 模式#

本文档描述了我们在组织和编写JupyterLab的CSS时所使用的模式。JupyterLab是使用一组位于packages中的npm包开发的。每个包都有自己的样式,但依赖于在主主题包中定义的CSS变量。

CSS 检查清单#

  • CSS 类名在代码中内联定义。我们过去将它们作为全大写的文件级 const,但我们正在逐渐放弃这种做法。

  • 包的CSS文件位于style子目录中,并导入到插件的index.css中。

  • theme-light-extensiontheme-dark-extension包中使用的JupyterLab默认CSS变量用于尽可能地对包进行样式设置。然而,各个包不应npm依赖这些包,以便能够更换主题。

  • 插件根据以下描述的约定,谨慎地定义了额外的公共/私有 CSS 变量。

CSS 变量#

我们正在JupyterLab中使用原生CSS变量。这是为了实现内置和第三方插件的动态主题化。截至2017年12月,所有流行浏览器的最新稳定版本都支持CSS变量,除了IE。如果JupyterLab部署需要支持这些浏览器,可以使用服务器端的CSS预处理器,如Myth或cssnext。

CSS变量的命名#

我们使用以下约定来命名CSS变量:

  • 所有CSS变量以--jp-开头。

  • 变量名称中的单词应为小写,并用-分隔。

  • 下一部分应引用组件和子组件,例如 --jp-notebook-cell-

  • 下一部分应引用任何状态修饰符,例如 active, not-activefocused: --jp-notebook-cell-focused.

  • 最后一部分通常与CSS属性相关,例如 color, font-sizebackground: --jp-notebook-cell-focused-background.

公共/私有#

JupyterLab 中的一些 CSS 变量被视为我们公共 API 的一部分。 其他变量被视为私有变量,不应由第三方插件或主题使用。公共变量和私有变量之间的区别很简单:

  • 所有私有变量都以 --jp-private- 开头

  • 所有没有private-前缀的变量都是公共的。

  • 公共变量应在:root伪选择器下定义。这确保了公共CSS变量可以在浏览器的开发者工具中的顶级标签下进行检查。

  • 在可能的情况下,私有变量应定义并限定在除:root之外的适当选择器下。

CSS 变量使用#

JupyterLab 在文件 packages/theme-light-extension/style/variables.css 中包含了一组默认的 CSS 变量。

为了确保JupyterLab中的设计一致性,所有内置和第三方扩展应尽可能在其样式中使用这些变量。关于这些变量的文档可以在variables.css文件本身中找到。

插件可以自由地在它们自己的index.css文件中定义额外的公共和私有CSS变量,但应该谨慎使用。

再次,我们认为这个包中的公共CSS变量的名称是我们的CSS公共API。

文件组织#

我们正在以下列方式组织我们的CSS文件:

  • 顶层packages目录中的每个包应包含在style子目录中所需的任何CSS文件,以进行自身样式设置。

  • 所有本地样式应整合到一个style/base.css文件中。

  • 顶层的 index.css 文件由 buildutils 作为 integrity 脚本的一部分进行模板化。它按依赖顺序导入 CSS,最后导入本地的 ./base.css。外部库的 CSS 由它们在 package.json 中的 style 字段决定。如果需要额外的文件或外部库没有 style 字段,我们使用 jupyterlab: { "extraStyles": { "fooLibrary": ["path/to/css"] } } 模式在我们的 package.json 中声明它们。对于不应添加到 index.css 的导入,更新 buildutils/src/ensure-repo.ts 中的 SKIP_CSS

CSS 类名#

CSS 类命名规范#

我们有一个相当正式的方法来命名我们的CSS类。

首先,CSS 类名与扩展 lumino.Widget 的 TypeScript 类相关联:

每个此类小部件的 .node 应该有一个与 TypeScript 类名称匹配的 CSS 类:

class MyWidget extends Widget {

  constructor() {
    super();
    this.addClass('jp-MyWidget');
  }

}

其次,子类应该为父类和子类都有一个CSS类:

class MyWidgetSubclass extends MyWidget {

  constructor() {
    super(); // Adds `jp-MyWidget`
    this.addClass('jp-MyWidgetSubclass');
  }

}

在这两种情况下,带有大写字母的CSS类名被保留用于存在命名的TypeScript Widget 子类的情况。这些类是TypeScript类为样式提供公共API的一种方式。

第三,Widget的子节点应该在CSS类名中有一个第三部分,用于给组件提供语义命名,例如:

  • jp-MyWidget-toolbar

  • jp-MyWidget-button

  • jp-MyWidget-contentButton

一般来说,父级MyWidget应该将这些类添加到子级中。这适用于子级是普通DOM节点或Widget实例/子类本身的情况。因此,CSS类的通用命名形式为jp-WidgetName-semanticChild。这使得这些子级的样式可以独立于它们的实现或它们自身的CSS类。

第四,一些CSS类用于修改小部件的状态:

  • jp-mod-active: 应用于处于活动状态的元素

  • jp-mod-hover: 应用于悬停状态的元素

  • jp-mod-selected: 在选择时应用于元素

第五,一些CSS类用于区分不同类型的小部件:

  • jp-type-separator: 应用于作为分隔符的菜单项

  • jp-type-directory: 应用于文件浏览器中的目录元素

边界情况#

随着时间的推移,我们发现这些规则并不能完全解决一些边缘情况。在这里,我们试图澄清这些边缘情况。

父元素何时应该向子元素添加类?

在上面,我们声明了一个父级(MyWidget),应该向子级添加CSS类,以指示子级的语义功能。因此,Widget的子类MyWidget应该向自身添加jp-MyWidget,并向工具栏子级添加jp-MyWidget-toolbar

如果子元素本身是一个Widget并且已经有了一个合适的CSS类名,比如jp-Toolbar,为什么不使用选择器如.jp-MyWidget .jp-Toolbar.jp-MyWidget > .jp-Toolbar呢?

原因是这些选择器依赖于工具栏的实现,该工具栏具有jp-Toolbar CSS类。当MyWidget添加jp-MyWidget-toolbar类时,它可以独立于其实现来样式化子元素。添加jp-MyWidget-toolbar类的另一个原因是,如果DOM结构高度递归,通常的后代选择器可能不足以仅针对所需的子元素。

当有疑问时,父母为孩子添加选择器几乎没有害处。

常用的CSS选择器#

我们使用CSS选择器来决定显示哪些上下文菜单项以及在使用键盘快捷键时调用什么命令。以下常见的CSS选择器旨在用于添加上下文菜单项和键盘快捷键。

针对小部件及其子元素的CSS类

  • jp-Activity: 应用于主工作区中的元素

  • jp-Cell: 应用于单元格

  • jp-CodeCell: 应用于代码单元格

  • jp-CodeConsole: 应用于控制台

  • jp-CodeConsole-content: 应用于控制台中的内容面板

  • jp-CodeConsole-promptCell: 应用于控制台中的活动提示单元格

  • jp-DirListing-content: 应用于文件浏览器目录列表的内容

  • jp-DirListing-item: 应用于文件浏览器目录列表中的项目

  • jp-FileEditor: 应用于文件编辑器

  • jp-ImageViewer: 应用于图像查看器

  • jp-InputArea-editor: 应用于单元格输入区域的编辑器

  • jp-Notebook: 应用于笔记本

  • jp-SettingEditor: 应用于设置编辑器

  • jp-SideBar: 应用于侧边栏

  • jp-Terminal: 应用于终端

描述小部件状态的CSS类

  • jp-mod-current: 仅应用于当前文档的元素

  • jp-mod-completer-enabled: 应用于可以托管自动补全功能的编辑器

  • jp-mod-commandMode: 应用于命令模式下的笔记本

  • jp-mod-editMode: 应用于编辑模式下的笔记本

  • jp-mod-has-primary-selection: 应用于具有主要选择的编辑器

  • jp-mod-in-leading-whitespace: 应用于在行首空白处有选择的编辑器

  • jp-mod-at-line-beginning: 应用于可以托管补全器并且在行首空白处有选择的编辑器

  • jp-mod-tooltip: 当页面上存在工具提示时应用于主体

针对数据属性的CSS选择器

  • [data-jp-code-runner]: 应用于可以运行代码的小部件

  • [data-jp-interaction-mode="terminal"]: 当代码控制台处于终端模式时应用

  • [data-jp-interaction-mode="notebook"]: 当代码控制台处于笔记本模式时应用

  • [data-jp-isdir]: 用于描述文件浏览器项目是否为目录

  • [data-jp-undoer]: 应用于可以撤销的小部件

  • [data-type]: 用于描述元素的类型,例如“文档标题”、“子菜单”、“内联”