@choiceform/ui-foundation

UI foundations for the choice form UI framework.

Usage no npm install needed!

<script type="module">
  import choiceformUiFoundation from 'https://cdn.skypack.dev/@choiceform/ui-foundation';
</script>

README

@choiceform/ui-foundation

简介

ui-foundation 是整个 choice-ui 套件的根本基础组成,它为其他 ui 组件以及最终的应用程序提供最不可缺少的基本样式系统和编写样式需要用到的工具集合、变量、可重用模块定义等等;同时它也提供了诸如 normalize.css 之类的基础第三方资源(可选)。

特性

| ------------------------ | :--------: | :------: | :------: | | ember-css-modules | 样式系统 | ✓ | ✓ | | bootstrap v4 reboot | 第三方资源 | ✓ | - | | normalize.css | 第三方资源 | ✓ | ✓ | | device.js | 第三方资源 | ✓ | ✓ |

  • postcss-import:用于导入其他 CSS 可重用模块。注意:虽然用法和看起来都和传统 CSS 的 @import 非常接近,但它们不是一样的东西。最大的差别在于 postcss-import 可以直接引用到位于 node_modules 下的 npm package,这是我们得以在 addon 之间共享资源的基础所在

    因为 addon 和 dummy 是不同的目录结构,因此解析可以 import 的样式规则也略有区别,ui-foundation 保持了绝大多数的引用规则一致,下面这张表格解释了在各种情况下如何引用来自 ui-foundation 提供的样式组件:

    项目 针对自身 针对 dummy 项目
    ui-foundation @import "./extends"; @import "extends";
    其他 addon @import "extends"; @import "extends";
    其他 app @import "extends"; 没有 dummy

    此外,尽管 postcss-import 也能像普通的 @import 一样导入各种样式文件,但是 ui-foundation 不推荐这么做。原因是所有的 postcss 插件其实都是 CSS Modules 技术栈的组成部分,往往引用的第三方样式并不希望把类名本地化,因此会出现出乎意料的副作用。对于第三方样式,比如 normalize.css 等,ui-foundation 都是使用 ember-cli-node-assets 导入的,不和 CSS Modules 技术栈牵扯。

    简言之,postcss-import 在 ui-foundation 中的作用主要是引用可复用的 extends(下面介绍),尽可能不要用于其他场合。

  • postcss-nesting:用于实现兼容 CSS 标准的嵌套样式解析。

  • postcss-extend:用于实现类似于 sass 的 placeholder 功能。注意:placeholder 和 mixin 开起来很像,但它们有本质的不同。mixin 是扩展目标选择符的内容,而 placeholder 则是定义了可共享的内容,扩展 placeholder 的选择符会合并在一起,而不是各自分开。在实践中,placeholder 适用于固定规则的可重用样式片段,由于它不会复制自身所以非常节省最终样式的尺寸;而 mixin 最大的优点是可以传递变量动态生成不同的内容,但并不适合共享固定规则的样式片段,因为它会不断复制自己。

    /* 对于 ui-foundation 自己而言 */
    @import "extends";
    
    /* Example 1 */
    .alpha {
      color: seagreen;
      @extend abs-center;
    }
    
    /* 结果:*/
    ._alpha_15x03s {
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    /* Example 2 */
    .alpha {
      color: seagreen;
      @extend abs-center;
    }
    
    .beta {
      @extend abs-center;
      color: skyblue;
    }
    
    /* 结果:*/
    ._alpha_15x03s, ._beta_15x03s {
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    ._alpha_15x03s {
      color: seagreen;
    }
    
    ._beta_15x03s {
      color: skyblue;
    }
    
    /* Example 3 - 你也可以像 less 那样直接 extend 一个 class */
    .alpha {
      color: seagreen;
      @extend abs-center;
    }
    
    .beta {
      @extend .alpha;
      color: skyblue;
    }
    
    /* 结果:*/
    ._alpha_15x03s, ._beta_15x03s {
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    ._alpha_15x03s, ._beta_15x03s {
      color: seagreen;
    }
    
    ._beta_15x03s {
      color: skyblue;
    }
    
    /* Example 4 - 利用例 3 的特点,extend 非常适合扩展子选择符 */
    .alpha {
      @extend abs-center;
    }
    
    .alpha:first-child {
      color: seagreen;
    }
    
    .alpha a:active {
      color: skyblue;
    }
    
    .beta {
      @extend .alpha;
    }
    
    /* 结果 */
    ._alpha_15x03s, ._beta_15x03s {
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    ._alpha_15x03s:first-child, ._beta_15x03s:first-child {
      color: seagreen;
    }
    
    ._alpha_15x03s a:active, ._beta_15x03s a:active {
      color: skyblue;
    }
    

    ui-foundation 包含的 extends.css 是一个定义 placeholder 的例子,有两种定义方法:

    /* 定义 */
    @define-extend placeholder {
      /* css */
    }
    /* 使用 */
    .alpha {
      @extend placeholder;
    }
    
    /* 定义 */
    %placeholder {
      /* css */
    }
    /* 使用 */
    .alpha {
      @extend %placeholder;
    }
    

    它们的区别主要是:后者拥有前者所有的特性,同时,后者可以1)定义可以晚于使用;2)后者可以重复扩展,也就是用 placeholder 去扩展 placeholder,从而构造组合片段;3)定义和引用的时候名字前面必须有 % 符号;4)可以在嵌套规则内定义和使用,比如 @media 内等等。因此,ui-foundation 推荐一般使用第二种定义方法。

  • post-css-variables:用于定义 CSS 变量。注意:因为我们需要跨项目的共享/覆盖变量设置,因此 ui-foundation 的 CSS 变量不是写在 .css 文件里,而是写在构建脚本的配置项里。defaults.js 提供的是这些变量的默认值,在其他 addon/app 里增加/覆盖它们的方法在安装说明部分有更具体的描述。

    /* 假设已有预先定义好的全局变量 --primary: seagreen; */
    
    /* 输入 */
    .alpha {
      color: var(--primary);
    }
    
    /* 输出 */
    .alpha {
      color: seagreen;
    }
    
    /* 也可以在局部定义/覆盖/引用,例如:*/
    .alpha {
      --primary: skyblue; /* 局部覆盖全局的变量 */
      color: var(--primary);
    
      --secondary: lightgray; /* 局部定义尚未存在的变量 */
      background-color: var(--secondary);
    }
    
    /* 结果:*/
    .alpha {
      color: skyblue;
      background-color: lightgray;
    }
    
  • postcss-custom-media:用于自定义媒体查询规则。ui-foundation 预置了一些常用的媒体查询规则,你可以选择覆盖或是添加新的规则(参见后面的自定义配置部分)。下面是一些示例:

    /* 输入 */
    @media (--mobile) {
      /* ... */
    }
    
    @media (--over-mobile) {
      /* ... */
    }
    
    @media (--tablet) {
      /* ... */
    }
    
    /* 输出 */
    @media (max-width: 425px) {
      /* ... */
    }
    
    @media (min-width: 426px) {
      /* ... */
    }
    
    @media (max-width: 768px) and (min-width: 426px) {
      /* ... */
    }
    
  • postcss-media-minmax:用于支持 CSS Media Queries Level 4 的新语法,例如:

    /* 输入 */
    @media screen and (width >= 500px) and (width <= 1200px) {
      /* ... */
    }
    
    /* 输出 */
    @media screen and (min-width: 500px) and (max-width: 1200px) {
      /* ... */
    }
    

    ui-foundation 预置的媒体查询规则是由此插件和 postcss-custom-media 配合定义的。

  • postcss-calc:用于对 calc() 函数进行预求值,例如(更多例子):

    /* 输入 */
    body {
      font-size: var(--main-font-size);
    }
    
    h1 {
      font-size: calc(var(--main-font-size) * 2);
      height: calc(100px - 2em);
      margin-bottom: calc(var(--main-font-size) * 1.5);
    }
    
    /* 输出 */
    body {
      font-size: 16px
    }
    
    h1 {
      font-size: 32px;
      height: calc(100px - 2em);
      margin-bottom: 24px
    }
    
  • postcss-short:用于转换一些常用的缩写形式,具体用法可以参见插件文档

  • postcss-clearfix:用于将 clear: fix; 规则转换为清除浮动的最佳实践规则。

  • postcss-fallback:提供 fallback 函数,用于优雅降级,例如:

    /* 输入 */
    .foo {
      display: fallback(flex, inline-block);
      width: fallback(45vh, 450px);
    
      background-color: fallback(rgba(0, 0, 0, 0.5), #555555);
      foo: fallback(bar, baz, qux, corge);
    }
    
    /* 输出 */
    .foo {
      display: inline-block;
      display: flex;
      width: 450px;
      width: 45vh;
    
      background-color: #555555;
      background-color: rgba(0, 0, 0, 0.5);
      foo: corge;
      foo: qux;
      foo: baz;
      foo: bar;
    }
    
  • postcss-color-function:用于支持 CSS 颜色转换函数,例如:

    /* 输入 */
    body {
      background: color(red a(90%))
    }
    
    /* 输出 */
    body {
      background: rgba(255, 0, 0, 0.9)
    }
    

    该插件支持很多种颜色相关的转换函数,具体用法可以参见插件文档

  • postcss-merge-rules:很多 PostCSS 插件会生成分散的样式规则,比如说:

    /* 输入 */
    .alpha {
      color: seagreen;
      @extend %abs-center;
    }
    
    /* 输出 */
    ._alpha_15x03s {
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    ._alpha_15x03s {
      color: seagreen;
    }
    

    这个插件的作用就是将这样的结果合并起来,提高可阅读性并减少文件体积。其最终结果是:

    ._alpha_15x03s {
      display: flex;
      justify-content: center;
      align-items: center;
      color: seagreen;
    }
    
  • postcss-discard-empty:用于剔除空的样式声明。空声明主要来自于嵌套语法,比如说:

    .parent { /* 仅作为命名空间使用 */
      &-alt { /* ... */ }
      & .child { /* ... */ }
    }
    
    /* 结果会是 */
    .parent {} /* 这就是应该剔除的空声明 */
    .parent-alt { /* ... */ }
    .parent .child { /* ... */ }
    
  • postcss-discard-comments:用于剔除 CSS 注释。如果有想要在最终结果中保留的注释,可以这样写:

    /*! 不要删我 */
    
  • postcss-discard-duplicates:用于合并重复的样式声明,比如:

    /* 输入 */
    h1 {
      margin: 0 auto;
      margin: 0 auto
    }
    
    h1 {
      margin: 0 auto
    }
    
    /* 输出 */
    h1 {
      margin: 0 auto;
    }
    

第三方资源

  • bootstrap reboot: Bootstrap v4 提供的现代样式一致化/常态化库,替代 normalize.css 作为默认使用的方案
  • normalize.css:几乎所有前端项目都会使用的样式一致化/常态化库,它的目的是为了实现默认样式的跨浏览器统一性(可选)
  • device.js:用于探测设备并为 HTML 文档添加识别用的样式命名空间,同时提供了 JavaScript 接口来访问设备的信息 ⚠️ 注意:ui-foundation 将 device.js 注册为全局对象,可以直接使用全局对象 window.device 进行引用。如果特定的组件有封装的需求,可以禁止 ui-foundation 加载 device.js 然后自己实现对 device.js 的封装。

UI 基础构件

  • Helpers
    • icon:用于渲染基于 SVG 的图标,或者是引用图片作为 CSS 背景的图标。

安装

❗️ 首先请确保全局安装的 Ember CLI 版本 >= 2.13.0

可以通过执行 yarn global upgrade 升级已有的 Ember CLI

或是通过执行 yarn global add ember-cli 来安装最新的 Ember CLI

先用 ember new / addon NAME 来生成一个新的 Ember Application / Addon,然后进入这个项目的目录(或者是已经存在的项目,但前提一定要确保依赖的 Ember CLI 版本 >= 2.13.0)。然后执行:

$ ember install @choiceform/ui-foundation

该命令执行后,会自动判断当前的项目类型然后执行一些命令来安装对应的依赖包。执行完毕之后,请留意最后给出的提示:

  • 对于 Ember Application,需要接着执行:ember generate foundation-app
  • 对于 Ember Addon,需要接着执行:ember generate foundation-addon

最后,运行 ember server 即可。也可以继续看下面的配置部分来对你的项目进行自定义调整。

配置

默认约定配置(无须调整的部分)

ui-foundation 提供了一个 defaults.js 文件,里面包含了所有可配置项的默认值。这个文件并不包含在默认蓝本之中,因此你的 addon/app 是不会看见这个配置文件的(除非该 addon 还提供专属的配置文件,其使用方法应参考该 addon 的文档)。

如果要引用这个文件的内容,可以这么做:

// 通常都是项目下的 ember-cli-build.js 文件里:
const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');

const options = require('@choiceform/ui-foundation/defaults')({
  // 这里编写自定义 PostCSS 插件选项,具体说明可以参考 defaults.js 内的详细注释
});

const merge = require('ember-cli-lodash-subset').merge;
module.exports = function(defaults) {
  const options = merge({}, defaultOptions, {
    // 这里编写你需要覆盖的配置,以 useDeviceJS 为例:
    useDeviceJS: false,
  });

  const app = new EmberAddon(defaults, options);

  return app.toTree();
};

由于该配置文件保存的是默认值,所以直接修改它的内容是没有意义的。如果你要覆盖默认的配置就使用 lodash.defaultsdeep 模块来合并自定义配置选项和默认选项。默认蓝本生成的 ember-cli-build.js 文件里演示了这个用法。

💡提示:这些配置是在 ember-cli 构建系统启动时被读取和加载的,它们仅针对 node 环境而存在,因此在你的应用程序里引用/修改它们是毫无意义的。

某些 addon 会提供额外的,存放于 config/enviroment.js 文件内的配置选项,这些是用于运行时环境的,是可以随时读取甚至修改的,不要把这二者搞混。

可配置项说明

可用于定制化的配置选项存放在 defaults.js 文件内,内有详细的注释简介,请自行参考并按上文的方法进行定制化配置。