最近学习了CSS的继承属性。正好看到这篇文章,所以翻译了一下。作者的想法在平时的项目中都或多或少的用过,但是我从来没有认真思考过如何利用这些特性让代码更优雅。我喜欢模块化设计。长期以来,我将网站划分为组件,而不是页面,并将这些组件动态合并为界面,提高了弹性、效率和可维护性。但我不希望我设计的东西看起来像是无关的东西,我正在做一个页面,而不是超现实的蒙太奇。幸运的是,有一种技术叫做CSS,它就是为解决上述问题而设计的。CSS可以通过样式化HTML组件以最小的成本确保设计的一致性。这要归功于CSS的两个主要特性:继承和级联cascade(CSS中的C)。虽然这些特性可以保证web文档的样式代码高效和DRY,而这也成为了CSS存在的理由,但它们显然并不流行。一些封装的CSS模块,比如BEM和AtomicCSS,都在极力避免或抑制这些特性,这让开发者可以更多地控制这些CSS,但这只是一种频繁的特殊干预机制。我将怀着尊重模块化接口设计的心重新审视继承、级联和作用域。目的是向读者展示如何平衡这些特性,使我们的CSS代码更加简洁灵活,界面也更加易于扩展。继承和font-family虽然很多人提出为什么CSS不只提供全局作用域,但是如果有的话很多东西就会变得重复。相反,CSS提供了全局作用域和局部作用域。就像在JavaScript中,局部作用域可以访问父作用域和全局作用域一样,CSS中的局部作用域促进了继承。例如,如果font-family在根(全局)html元素中声明,这可确保此规则将应用于文档中的所有祖先元素(下一节将讨论一些例外情况)。就像在JavaScript中一样,如果我将一个变量声明为局部作用域,那么它在全局或任何祖先作用域中都是无效的,但它可以作用于它的子域(例如上面代码中的p元素)。在下一个示例中,1.5的行高不适用于html元素,而p元素内的a元素继承了p的行高值。继承的伟大之处在于你可以用很少的代码建立一个一致的视觉设计基础,而且样式甚至会影响你还没有写过的HTML。这些是面向未来的代码。备选方案当然,有很多方法可以定义通用样式。例如,我可以创建一个.sans-serif类并将其应用于我认为将使用此样式的任何元素:这样我可以准确选择哪些元素需要此样式,哪些元素不需要。任何控制的机会都很诱人,但也存在明显的问题。在这种情况下,我们不仅需要手动为所需的元素添加类名(这意味着这些类名必须从一开始就知道),也意味着我们放弃了支持动态内容的可能性:所见即所得的编辑器和没有一个Markdown解析器可以为默认的p元素提供无衬线类。除了class="sans-serif"需要在样式表和HTML中都添加代码外,与style="font-family:sans-serif"没有太大区别。如果我们使用继承,我们可以写更少的代码,甚至可以不写其他代码。相比于为每种字体样式都写一个类名,我们只需要在html元素中声明我们想要的任何样式即可:inherit关键字的某些属性默认值为不inherit,某些元素不继承某些属性。在某些情况下,我们可以使用[属性名]:inherit来判断继承。例如,input和textarea元素不继承字体的任何属性。为了确保所有元素都从全局范围继承这些属性,我们可以使用全局选择器和关键字inherit。这样就可以最大程度的利用继承。请注意,我没有设置font-size的值。我不希望直接继承font-size,这样可能会覆盖一些元素默认的user-agent样式,比如header元素,small元素等,这样我就可以省下一行代码让用户代理决定样式。我不想继承的另一个属性是font-style:我不想重新定义em的斜体,这会浪费工作时间并导致更多代码。现在,所有可以继承或强制继承的字体样式都按我的意愿实现了。现在只需要两个块来声明一致性和范围。从此以后,开发者在构建组件时甚至不用考虑font-family、line-height、color,除非遇到一些特殊情况,需要级联。基于异常的样式我可以使用继承来为主标题实现相同的字体系列、颜色和行高。但是当我想让font-size的值不同时,我可能不需要设置任何东西,因为user-agent已经为h1元素增加了更大尺寸的font-size(是base的125%我们设置的字体)。但是如果我想调整元素的字体大小怎么办?这时候我就可以利用全局作用域,只在局部作用域调整我需要调整的元素。如果默认封装了CSS元素的样式,那么这种情况是不可能的:将所有的字体样式都添加到h1中。相反,我可以将我的样式分成单独的类,并将它们添加到每个h1中,以空格分隔。无论哪种方式,它都是更多的工作并且只能产生一个具有固定样式的h1。如果你使用级联,你可以按照我想要使用的方式定义大多数元素的样式。其实h1只是一个特例。级联就像一个过滤器,这意味着只有在添加新样式时才会覆盖旧样式。元素样式我们有了一个良好的开端,但要真正掌握层叠,我们需要为尽可能多的常用元素定义样式。为什么?由于我们的混合插件由单独的HTML元素组成,因此屏幕阅读器友好的界面应该利用标记的语义结构。构成界面的“分子”的“原子”(原子设计术语)可以通过元素选择器定位。元素选择器具有低优先级,因此它们不会覆盖任何基于类的选择器样式。我们需要做的第一件事是为我们需要使用的所有元素定义样式。如果想更简洁的实现统一的界面,接下来的步骤很重要:每次需要创建一个新的组件,如果引入新的元素,那么使用元素选择器来定义这些新元素的样式。现在你不需要使用限制性的、高优先级的选择器,也不需要添加类名来定义样式,只需要使用语义元素本身。例如,如果我没有定义按钮的样式(如上例),而我的新组件包含一个按钮元素,那么我需要为整个界面的按钮元素定义样式。现在,如果您需要编写一个新组件但具有相同的按钮,那么就少了一件需要担心的事情。您不需要在不同的命名空间中重写相同的CSS,也不需要记住或编写类名。CSS设计的目的是让事情变得更简单、更高效。使用元素选择器有以下优点:避免HTML的冗长(没有多余的类)。避免样式表的冗长(不同的组件可以共享样式,不需要每个组件都重写)。最终界面具有语义HTML结构。使用类来提供独特的样式通常被定义为“关注点分离”,这是对W3C关注点分离原则的误解,其目的是使用HTML和CSS样式来描述整个结构。类专门用于指定样式并出现在结构中,因此无论它们出现在哪里,它们在技术上都打破了分离。您必须更改自然结构才能获得样式。如果你不依赖表面结构标记(类、内联样式),你的CSS将兼容常见的结构并符合语法规范。这使得扩展内容和功能变得无关紧要,并且不需要将其变成样式任务。它还使您的CSS能够被具有传统语义结构的不同项目重用(CSS“方法论”是另一回事)。特例之前有人建议我想的太简单了,我意识到界面上并不是所有的按钮都做同样的事情,我也意识到不同功能的按钮看起来可能不同。这并不意味着我需要定义类、继承或级联。使界面按钮看起来完全不同会使您的用户感到困惑。为了确保可访问性和一致性,许多按钮只需要在外标签上进行区分。请记住,风格并不是唯一的视觉区分方式。内容也可以提供视觉上的区分,这种方式会更直观,可以直接从文字中了解它们之间的区别。有必要或适合单独使用样式来区分内容的场景可能比您想象的要少。通常,区分样式是补充性的,例如红色背景或带有图标的文本标签。文本标签的存在对使用语音激活的软件有特殊影响:说“红色按钮”或“带十字图标的按钮”是不可能让软件识别的。我将在“实用程序类”部分向您展示如何向外观相似的元素添加细节。AttributesSemanticHTML不仅指结构,标签特征还可以定义类型、样式属性和状态。这些对于可访问性都非常重要,因此需要在HTML中需要的地方编写。正因为它们出现在HTML中,它们为制作样式挂钩提供了额外的机会。比如input元素有一个type属性,你应该好好利用,再比如aria-invalid就是用来描述状态的。有几点需要注意:由于使用了inherit关键字,color、font-family、line-height继承了html的值,所以不需要设置。如果您想在整个应用程序范围内更改字体系列,只需在html块中声明一次即可。边框颜色是关联颜色的,所以也继承了全局颜色,我只需要设置边框的宽度和样式即可。属性选择器[aria-invalid]不受限制。这意味着它可以更好地应用(可以应用于输入和文本区域选择器),并且优先级较低。简单属性选择器与类选择器具有相同的优先级。不受限制地使用它们意味着任何在堆栈下方编写的类都可以覆盖它们。BEM方法可以利用修饰符类来解决这种方法,例如input-invalid。但是考虑到invalid状态只有在可通信的时候才能应用,input-invalid肯定是多余的。也就是说,aria-invalid这个属性是必须出现的,那个类的作用是什么?只是分层编写HTML,大量的元素和属性选择器绝对是我的最爱:新组件的构建变得,理解HTML结构比理解公司或组织的命名约定重要得多。分配到该项目的开发人员将受益于现有的继承样式,减少对参考文档或编写新CSS的需要。TimBaxter在MeaningfulCSS:StyleItLikeYouMeanIt中提到了这个案例。布局到目前为止,我们还没有编写额外的特定于组件的CSS,这并不意味着我们没有给它们任何样式。所有的组件都是由HTML元素组成的,更复杂的组件是由这些元素的排列顺序组成的。这就引出了布局的概念。我们主要需要处理流体布局——连续块级元素的间距。您可能已经注意到,到目前为止我还没有为元素设置任何边距。那是因为边距不应被视为元素的属性,而应被视为元素上下文的属性。也就是说,它们仅在元素相遇时起作用。幸运的是,邻接选择器可以准确描述这种关系。除了少数例外,级联允许您为出现在子元素中的所有块级元素设置统一的默认值。非常低优先级的lobotomizedowl选择器确保所有元素(除了通常的例外)都排在一行上。这意味着在所有情况下默认情况下都存在白色间距,并且所有开发流体布局的开发人员都有一个合理的起点。在大多数情况下,利润率只关心他们自己。但由于它的优先级较低,因此也很容易在需要时覆盖其行间距。例如,为了表示标签与其相关元素之间的关系,我可能需要缩小它们之间的距离。在下面的例子中,标签和它们后面的所有元素(input、textarea、select等)之间的空间将缩小。使用级联意味着只在需要的地方编写特定的样式,其余的则符合合理的基线。请注意,由于边距仅出现在元素之间,因此它们不会与容器的填充重叠。无论您是否包含容器元素都会产生相同的边距。这意味着,您可以实现如下所示的相同布局-与标签之间相比,div之间的这些边距看起来更好。为了使用原子CSS等方法获得相同的结果,可以手动组合具体的边距相关类,包括通过*+*隐式处理控制第一个孩子的特殊情况。请记住,如果一个元素使用原子CSS,它只会覆盖它的上边距。您需要分别为颜色、背景颜色或其他主要属性添加特定类,因为原子CSS不利用继承或元素选择器。AtomicCSS使开发人员可以完全控制样式,而无需内联样式(不能像类一样重用)。为单个属性提供类名可以减少样式表中的重复声明。但是,必须通过向结构添加标记来直接完成。这需要开发人员学习其冗长的API并添加大量额外的HTML代码。相反,如果您为任意HTML元素及其空间关系设置样式,CSS方法就会过时。与包含样式叠加的HTML系统相比,保持一致的设计系统将具有显着优势。不管怎样,这里是我们的CSS架构和流体布局解决方案需要具备的特性:全局(html)样式和强制继承;流体布局有一些例外(lobotomizedowl选择器);元素和属性样式。我们还没有编写特定的组件和CSS类,但是我们已经完成了大部分的样式设置——假设我们可以合理地编写可重用的类。实用程序类具有全局范围:无论它们在HTML中的何处应用,它们都会受到关联CSS的影响。这在很多情况下可以看作是一个缺点,因为两个开发人员分开工作可能会写一个同名的类,这会影响对方的工作。最近设想的CSS模块通过以编程方式生成在本地或组件内绑定到它们的唯一类名来解决这个缺点。忽略生成代码的丑陋表面,你可以很容易地看到独立编写的组件之间的直接区别:唯一标识符用于为相似的事物定义样式。这会导致界面不一致,或者通过更大的努力和更多的冗余来保持一致。将共同元素视为唯一元素是没有意义的。您应该为某些类型的元素定义样式,而不是特定的元素实例。请记住,“类”的意思是“可能包含更多内容的某种类型的事物”。换句话说,所有类都应该是实用类:全局可重用。当然,在这个例子中,类名.button是多余的:因为我们使用的是按钮元素选择器。但是特殊类型的按钮呢?比如我们可以写一个类名.danger来表示这样的按钮可以进行危险的操作,比如删除数据:因为类选择器的优先级高于元素选择器,而与属性选择器的优先级相同,类中写的任何规则选择器将覆盖元素选择器和属性选择器。上面的所有危险按钮都将显示为白色文本和红色背景,但其他属性(例如填充、轮廓和焦点边距)将保持不变。当多个开发人员长时间处理同一代码时,偶尔会发生命名冲突。但是有很多方法可以避免它,例如,哦,我不知道,首先要做的是搜索您要使用的名称,看看它是否存在。你永远不知道,有人可能已经解决了你试图解决的问题。本地作用域我最喜欢使用实用程序类做的事情是将它们设置在容器上,然后使用钩子来影响内部子元素的布局。例如,我可以为任何元素快速编写一个等距、响应式、居中的布局:这样,我可以将列表、按钮、按钮组合和链接居中。这要归功于>*的使用,这意味着.centered中的任何直接子元素都应用这些样式,继承该范围内的全局元素和父元素的样式。我已经调整了元素的边距,以便元素可以自由环绕,而不会破坏*+*选择器中设置的垂直布局。这样,通过设置任意元素的局部作用域,就可以用少量的代码提供一个通用的、响应式的布局方案。我的基于flexbox的小型网格系统(93Bgzipped)基本上只是一个实用程序类。它具有很高的可重用性,并且因为它利用了flex-basis,所以不需要断点介入。我只使用flexbox布局的方法。使用BEM,可以在每个网格上设置一个显式的“元素”类:但这不是必须的。只需要一个身份来实例化本地范围。与我的版本相比,这些主题不受外部影响的保护,也不应受到>*的影响。唯一的区别是有很多标记。现在,我们已经开始合并类,但只合并预期的通用样式。我们仍然不单独为复杂组件定义样式。相反,我们以可重用的方式解决系统级问题。当然,你需要在评论中说明如何使用这些类。这些实用程序类同时利用了CSS的全局作用域、局部作用域、继承和级联。这些类可以应用到任何地方,它们实例化局部作用域来影响子元素,从父元素或全局作用域继承样式,并且不需要过多地使用元素选择器或类选择器。我们的堆栈现在看起来像这样:全局(html)样式和强制继承;流体布局有一些例外(lobotomizedowl选择器);元素和属性的样式;公共实用程序类。当然,也有可能不需要像上面的例子那样编写实用类。但关键是,如果你需要使用一个组件,那么解决方案应该是针对所有组件的,你应该从系统的角度来思考问题。具体的组件样式我们已经定义了组件的样式以及组件的组合方式,所以很容易忽略这部分的内容。但值得一提的是,任何不是由其他组件创建的组件(包括单个HTML元素)都必须存在。它们是使用ID选择器的组件,可能会产生系统性风险。事实上,一个好的经验法则是只通过ID识别复杂的组件(“分子”、“有机体”),而不是在CSS中使用这些ID。例如,您可以在登录表单组件中标记#login,您不需要在具有元素、属性或流体布局样式的CSS中使用#login,即使您可能会发现您构建了一个通用的实用程序类或二。可用于其他组件。如果您使用#login,这只会影响该特定组件。这提醒您,您已经偏离了开发设计系统并转向大量冗长的代码工作。结论当我告诉大家我不使用BEM之类的方法或CSS模块之类的工具时,很多人会认为我就是这样写CSS的:但我没有。此处明确指出了过度规范,我们需要小心避免。就像BEM(OOCSS、SMACSS、原子CSS等)并不是避免复杂、难以管理的CSS的唯一方法。为了解决优先级问题,很多方法引入了类选择器。这样做的问题是它会导致一堆冗余类:使结构膨胀的奇异代码——不注意文档——并使进入系统的新开发人员感到困惑。通过广泛使用类,您可以最大限度地将样式文件与HTML系统分离。这不适合“关注点分离”,导致冗长或更糟糕的代码,导致无法访问:它可以影响视觉风格而不影响可访问性状态:除了大量编写和使用类之外,我还看到了以下方法:使用继承来建立一致的前提条件;使用最多的元素和属性选择器,支持透明、标准的基础组件;使用简单的流布局系统;结合一些常用的实用类来解决受多个元素影响的布局问题。所有这些方法都有助于创建可设计的系统,使编写新的界面组件变得更容易,并使成熟的项目减少对新CSS代码的依赖。这不是由于严格的命名和封装,而是缺少它们。即使你不是很习惯我在这里推荐的方法,我希望这篇文章至少能帮助你重新思考什么是组件。它们不是您孤立地创建的。有时,在标准HTML元素下,它们也不是您创建的。通过组件组合更多的组件可以让你的界面以更少的CSS具有高度的可访问性,并具有一致的视觉效果。CSS没有错。事实上,它可以让您用更少的代码做更多的事情。我们只是没有充分利用它。通过海顿皮克林
