[译] JavaScript 中的 CSS:基于组件的样式的未来

简介: 本文讲的是[译] JavaScript 中的 CSS:基于组件的样式的未来,使用行内样式使我们可以获得 JavaScript 的所有编程支持。这让我们获得类似 CSS 预处理器(变量、混入和函数)的好处,它也解决了 CSS 的很多问题,如全局命名空间和样式冲突。
本文讲的是[译] JavaScript 中的 CSS:基于组件的样式的未来,

JavaScript 中的 CSS:基于组件的样式的未来


使用行内样式使我们可以获得 JavaScript 的所有编程支持。这让我们获得类似 CSS 预处理器(变量、混入和函数)的好处,它也解决了 CSS 的很多问题,如全局命名空间和样式冲突。

如果想要更深入了解 JavaScript 中的 CSS 所解决的问题,可以查看著名的演示幻灯:React:JS 中的 CSS。有关使用 Aphrodite 性能优化的案例研究,你可以阅读 行内 CSS 在可汗学院:Aphrodite。如果想要学习更多有关 JavaScript 中的 CSS 的最佳实践,可以阅读Airbnb 的风格指南

此外,我们将使用行内 JavaScript 样式来构建组件,以解决我之前的一篇文章(掌握设计之前,必须掌握基本原理)中涉及的一些基础设计问题。

一个启发性的例子

让我们从一个简单的例子开始:构建一个按钮并给它添加样式。

一般来说,组件及其样式在同一个文件中:Button 和 ButtonStyles。这是因为他们都属于视图层。但是,下面的例子中,我将代码拆分成多个代码片段,以便更容易理解。

下面就是按钮组件:

...

function Button(props) {
  return (
    <input
      type="button"
      className={css(styles.button)}
      value={props.text}
    />
  );
}

它没什么特别的,只是一个无状态的 React 组件。Aphrodite 起作用的地方是在 className属性中。css 函数接受一个 styles 对象为参数并将其转换为 cssstyles 对象是由 Aphrodite 的函数 StyleSheet.create({ ... }) 创建的,你可以用 Aphrodite playground 来查看这个函数的输出结果。

下面是按钮的样式表:

...

const gradient = 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)';

const styles = StyleSheet.create({
  button: {
    background: gradient,
    borderRadius: '3px',
    border: 0,
    color: 'white',
    height: '48px',
    textTransform: 'uppercase',
    padding: '0 25px',
    boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .30)',
  },
});

Aphrodite 的优势之一是迁移很直观,学习曲线较平缓。类似 border-radius 变成borderRadius,值变成字符串,伪类选择器、媒体查询、字体定义都可以正常工作。另外也可以自动添加浏览器引擎前缀。

下面就是按钮的样子:

以这个例子为基础,让我们来看看如何使用 Aphrodite 来构建一个基本的视觉设计系统,着重关注排版和间距两个设计基础元素。

设计基础第一部分:排版

我们先从排版开始,这是设计基础要素。第一步是定义排版常数。与 Sass 及 Less 不一样,Aphrodite 的常数可以直接放在 JavaScript 中或 JSON 文件中。

定义排版常数

在定义常量时,使用语义化的变量名。例如,在给字体大小命名时,不要使用 h2,使用displayLarge 描述它的作用。类似的,不要给字体粗细命名 600,使用 semibold 描述它的效果。

export const fontSize = {
  // heading
  displayLarge: '32px',
  displayMedium: '26px',
  displaySmall: '20px',
  heading: '18px',
  subheading: '16px',

  // body
  body: '17px',
  caption: '15px',
};

export const fontWeight = {
  bold: 700,
  semibold: 600,
  normal: 400,
  light: 200,
};

export const tagMapping = {
  h1: 'displayLarge',
  h2: 'displayMedium',
  h3: 'displaySmall',
  h4: 'heading',
  h5: 'subheading',
};

export const lineHeight = {
  // heading
  displayLarge: '48px',
  displayMedium: '48px',
  displaySmall: '24px',
  heading: '24px',
  subheading: '24px',

  // body
  body: '24px',
  caption: '24px',
};

设置正确的字体大小和行高变量的值是很重要的。这是因为他们直接影响了设计的垂直韵律。垂直韵律是一个能帮助你实现一致的元素间距的概念。

想要了解更多有关垂直韵律的内容,你可以阅读这篇文章:为什么垂直韵律对排版实践很重要?

上图:行高计算器

选择行高以及字体大小的背后是有科学原理的。我们可以使用比率生成一组可能的值。几周前,我写了一篇文章,详细地介绍了方法细节(排版可以成就设计,也可以毁了设计)。你可以使用 Modular Scale 确定字体大小,使用 vertical rhythm calculator 计算行高。

定义标题组件

定义好了排版常量后,下一步就是使用它们创建一个组件。这个组件的目标是对整个代码库中的标题实现一致的设计

import React, { PropTypes } from 'react';
import { StyleSheet, css } from 'aphrodite/no-important';
import { tagMapping, fontSize, fontWeight, lineHeight } from '../styles/base/typography';

function Heading(props) {
  const { children, tag: Tag } = props;
  return <Tag className={css(styles[tagMapping[Tag]])}>{children}</Tag>;
}

export default Heading;

export const styles = StyleSheet.create({
  displayLarge: {
    fontSize: fontSize.displayLarge,
    fontWeight: fontWeight.bold,
    lineHeight: lineHeight.displayLarge,
  },
  displayMedium: {
    fontSize: fontSize.displayMedium,
    fontWeight: fontWeight.normal,
    lineHeight: lineHeight.displayLarge,
  },
  displaySmall: {
    fontSize: fontSize.displaySmall,
    fontWeight: fontWeight.bold,
    lineHeight: lineHeight.displaySmall,
  },
  heading: {
    fontSize: fontSize.heading,
    fontWeight: fontWeight.bold,
    lineHeight: lineHeight.heading,
  },
  subheading: {
    fontSize: fontSize.subheading,
    fontWeight: fontWeight.bold,
    lineHeight: lineHeight.subheading,
  },
 });

Heading 组件是一个无状态的函数,接收一个标签作为属性,并返回这个标签连带它的样式。我们在前面的常量中定义了标签映射,所以这是可行的。

...
export const tagMapping = {
  h1: 'displayLarge',
  h2: 'displayMedium',
  h3: 'displaySmall',
  h4: 'heading',
  h5: 'subheading',
};

在组件文件的下方我们定义了 styles 对象,我们就是在此处使用排版常量的。

export const styles = StyleSheet.create({
  displayLarge: {
    fontSize: fontSize.displayLarge,
    fontWeight: fontWeight.bold,
    lineHeight: lineHeight.displayLarge,
  },

  ...
});

Heading 组件是这样调用的:

function Parent() {
  return (
    <Heading tag="h2">Hello World</Heading>
  );
}

通过这种方法,我们可以减少类型的意外变化。通过取消全局样式以及标准化标题,我们避免了上百种字体大小的问题。此外,这种方法还可以应用于构建 Text 组件。

设计基础第二部分:间距

间距同时控制着设计中的垂直与水平韵律。所以间距对建立视觉设计系统至关重要。和排版部分一样,第一步也是设定间距常量。

定义间距常量

当为元素之间的 margin 定义间距常量时,我们可以采取一种数学方法。使用一个spacingFactor 常量来生成一组距离。这种方法确保元素之间的间距是有逻辑并且一致的

const spacingFactor = 8;
export const spacing = {
  space0: `${spacingFactor / 2}px`,  // 4
  space1: `${spacingFactor}px`,      // 8
  space2: `${spacingFactor * 2}px`,  // 16
  space3: `${spacingFactor * 3}px`,  // 24
  space4: `${spacingFactor * 4}px`,  // 32
  space5: `${spacingFactor * 5}px`,  // 40
  space6: `${spacingFactor * 6}px`,  // 48

  space8: `${spacingFactor * 8}px`,  // 64
  space9: `${spacingFactor * 9}px`,  // 72
  space13: `${spacingFactor * 13}px`, // 104
};

上面的例子采用了线性关系,从 1 到 13。不管怎样,多试验几种不同的尺度和比例的搭配才能找到合适的方案。目的、受众、目标设备的不同都需要在设计时考虑。下面是使用黄金比率计算出来的前 6 个距离,以 spacingFactor 等于 8 为例。

Golden Ratio (1:1.618)

8.0 x (1.618 ^ 0) = 8.000
8.0 x (1.618 ^ 1) = 12.94
8.0 x (1.618 ^ 2) = 20.94
8.0 x (1.618 ^ 3) = 33.89
8.0 x (1.618 ^ 4) = 54.82
8.0 x (1.618 ^ 5) = 88.71

下面是在代码中如何写间距比例。我添加了一个帮助处理间距计算结果的函数,它会返回其最近的像素值。

const spacingFactor = 8;
export const spacing = {
  space0: `${computeGoldenRatio(spacingFactor, 0)}px`,  // 8
  space1: `${computeGoldenRatio(spacingFactor, 1)}px`,  // 13
  space2: `${computeGoldenRatio(spacingFactor, 2)}px`,  // 21
  space3: `${computeGoldenRatio(spacingFactor, 3)}px`,  // 34
  space4: `${computeGoldenRatio(spacingFactor, 4)}px`,  // 55
  space5: `${computeGoldenRatio(spacingFactor, 5)}px`,  // 89
};

function computeGoldenRatio(spacingFactor, exp) {
  return Math.round(spacingFactor * Math.pow(1.618, exp));
}

定义好间距常量后,我们就可以用它们给元素添加间距。一种方法就是在组件中 import

例如,下面我们给 Button 组件添加 marginBottom

import { spacing } from '../styles/base/spacing';

...

const styles = StyleSheet.create({
  button: {
    marginBottom: spacing.space4, // 使用间距常量来添加 margin
    ...
  },
});

多数情况下这都是有效的。但是如果我们想要根据按钮的位置来修改它的 marginBottom 属性呢?

实现可变边距的一种方法是覆盖从父组件继承的样式。另一种方法是创建一个 Spacing 组件来控制元素的垂直边距

import React, { PropTypes } from 'react';
import { spacing } from '../../base/spacing';

function getSpacingSize(size) {
  return `space${size}`;
}

function Spacing(props) {
  return (
    <div style={{ marginBottom: spacing[getSpacingSize(props.size)] }}>
      {props.children}
    </div>
  );
}

export default Spacing;

这种方法可以将设置边距的任务从子组件转移到父组件上。这样,子组件就对布局无感知了,它不需要知道将被放置在何处及与其他元素的关联

由于按钮、输入框、卡片等组件可能需要可变的间距,所以这种方法是有效的。例如,表单中的按钮可能比导航栏的按钮需要更大的边距。需要注意的是,如果一个组件始终具有一致的边距,那么在组件内部处理边距更好。

你可能注意到前面的例子中只使用了 marginBottom ,这是因为在一个方向定义所有的垂直边距可以避免边距合并,并能跟踪垂直韵律。你可以从 Harry Robert 的文章 单向边距声明 中了解更多这方面知识。

最后,你还可以使用间距常量来定义 padding。

import React, { PropTypes } from 'react';
import { StyleSheet, css } from 'aphrodite/no-important';
import { spacing } from '../../styles/base/spacing';

function Card(props) {
  return (
    <div className={css(styles.card)}>
      {props.children}
    </div>
  );
}

export default Card;

export const styles = StyleSheet.create({
  card: {
    padding: spacing.space4}, // using spacing constants as padding

    background: 'rgba(255, 255, 255, 1.0)',
    boxShadow: '0 3px 17px 2px rgba(0, 0, 0, .05)',
    borderRadius: '3px',
  },
});

对 margin 和 padding 使用相同的间距常量,可以在设计中实现更好的视觉一致性。

结果大致如下:

现在你已经大致了解 JavaScript 中的 CSS 了,去试验一下吧。尝试在下个项目中采用行内 JavaScript 样式吧。我想你会喜欢上能够在同一个上下文中处理所有样式及视图问题的感觉

有关 CSS 和 JavaScript 的主题中,你对什么新的发展感兴趣呢?我个人对 async/await 非常感兴趣。给我留言或者在 Twitter 上发信息给我吧。

你可以在 Medium 上找到我,我每周都会发布一篇文章。你也可以在 Twitter 上关注我,我会在那里发布一些有关设计、前端开发和虚拟现实的随笔。






原文发布时间为:2017年5月02日

本文来自云栖社区合作伙伴掘金,了解相关信息可以关注掘金网站。
目录
相关文章
|
3天前
|
前端开发 JavaScript 索引
CSS常见用法 以及JS基础语法
CSS常见用法 以及JS基础语法
10 0
|
3天前
|
前端开发
css设置内嵌样式阴影
css设置内嵌样式阴影
7 0
|
3天前
|
前端开发
css样式实现一个滑动按钮
css样式实现一个滑动按钮
6 0
|
4天前
|
前端开发 UED
CSS 支持伪类和伪元素,可用于指定文档中不同状态的样式
CSS 提供关键帧和过渡动画两种方式创建动态效果。关键帧动画通过定义一系列样式的关键帧,浏览器自动插入过渡帧形成动画,如示例中背景颜色变化的循环。过渡动画则在属性改变时(如鼠标悬停)触发,展示平滑转换,如 div 元素尺寸变化。通过调整帧时间、顺序和样式,可实现更复杂的动画,增强网站交互体验。
23 4
|
5天前
|
JavaScript 前端开发
js和css以及js制作弹窗
js和css以及js制作弹窗
10 1
|
9天前
|
JavaScript 前端开发 API
Vue.js 中子组件向父组件传值的方法
Vue.js 中子组件向父组件传值的方法
23 2
|
10天前
|
编解码 前端开发 UED
【专栏:HTML 与 CSS 移动端开发篇】CSS 媒体查询与移动端特定样式
【4月更文挑战第30天】CSS媒体查询在移动端开发中至关重要,它基于设备特性(如屏幕尺寸、分辨率、方向)应用特定样式,实现响应式设计。通过`@media`规则定义条件,如`(max-width: 600px)`,当屏幕宽度小于或等于600px时应用相应样式。常见条件包括屏幕宽度、高度、方向和分辨率。媒体查询可用于响应式布局、导航菜单优化、图片加载及字体调整。在实践中,需注意保持查询简洁,充分测试,渐进增强,并提前规划。掌握媒体查询能提升移动端用户体验,创造更优秀的网页设计。
|
10天前
|
开发框架 前端开发 搜索推荐
标题:【专栏:CSS进阶篇】CSS样式重置与框架:快速构建统一风格的网页
【4月更文挑战第30天】本文探讨了CSS样式重置和框架在确保网页跨浏览器一致性中的作用。样式重置通过消除默认样式差异实现一致外观,而CSS框架如Bootstrap提供预设样式和组件,加速开发并保证页面一致性。框架还有响应式设计和易于维护的优点,但也可能限制自定义和增加性能开销。选择使用哪种工具应根据项目需求、团队技能和设计复杂度来决定。开发者可结合使用两者以平衡灵活性和控制。
|
10天前
|
前端开发 UED
【专栏:CSS 基础篇】CSS 字体与文本样式:美化你的网页内容
【4月更文挑战第30天】网页设计中,字体和文本样式至关重要,影响视觉效果和用户体验。CSS允许设计师设置字体家族、大小、颜色、加粗、倾斜、行高和对齐方式等。高级特性包括引入外部字体和使用字体变体。响应式设计适应不同设备,确保良好阅读体验。实际案例和最佳实践强调易读性和一致性。掌握这些技巧能提升网页美感和用户交互,创造更多可能。
|
10天前
|
前端开发 UED
【专栏:CSS 基础篇】CSS 入门:给网页添加样式
【4月更文挑战第30天】CSS是网页设计的关键,用于控制网页样式和布局。它通过选择器(如元素、类和ID)来设定颜色、字体、对齐、背景等属性。CSS可内嵌、外部引用或行内设置。布局技巧包括浮动、定位和弹性盒子。响应式设计利用媒体查询适应不同设备。不断实践和学习CSS,能提升网页的吸引力和用户体验。一起探索CSS的魅力吧!

相关实验场景

更多