解耦模块的CSS最佳实践

时间:2015-06-17 21:57:01

标签: css

这个问题的答案并没有指出一些我不知道的css选择器或属性。它也没有把一些随机的css放在一起使得这个特定的案例有效。我可以想到几个让这个具体例子起作用的方法。我确定还有数百个。

创建CSS的最佳做法是什么,以便各种设计元素分离?

我的意思解释:

我是一名具有良好设计感的计算机程序员。在编写好的代码时,我希望创建解耦的类/对象,这意味着它们之间没有奇怪和意外的交互。我可以自由地混合和匹配我的类/对象,结果运行良好,是您所期望的。我学习/创建CSS最佳实践的所有尝试都不能很好地解决这个问题。我已经成为一名.NET Web开发人员已有10多年了。很长一段时间我都相信语义CSS。我喜欢csszengarden.com。我试图学习OOCSS和SMACSS。尽管如此,我仍然可以通过我的代码工作方式来获取CSS。我在网上搜索CSS最佳实践,并找到命名,格式化和一些提示和技巧。从未深入了解如何创建解耦的CSS。也许这是不可能的。我觉得不应该这样。我觉得应该可以创建一个可以组合的可重用元素的设计语言。

因为所有这些都是非常抽象的,如果没有例子就很难讨论。以下是我遇到的挑战的一个例子。这是基于涉及bootstrap的情况,但我简化了样式。所以请理解它们的样式是因为这对于网站的其他部分是有意义的,而这个例子并不是关于一些微不足道的变化,这使得它在这种情况下起作用。

示例:

此代码在jsbin上。

我有一个带标题和内容的面板模块。通常,标头包含h2和一个或多个按钮操作:

Basic Module

注意,标题周围的等填充和动作向右浮动。此设计具有响应性,当面板较窄时,操作必须低于标题。虽然实际上存在一个问题,但标题和按钮之间没有空格。

但实际上,面板是一个模块,它可以包含任何标题。这应该遵循OOCSS原则,即容器和内容的分离"。因此,我们放在面板标题中的内容并不重要。我们希望它运作良好。

现在在特定页面上将选择列表放在面板标题中是有意义的。与Bootstrap一样,有很多与表单有关的样式,所以我们也在这里使用这些样式。结果如下所示:

Module with Form in Header

请注意,因为表单组(每个Bootstrap)具有底部边距,所以现在标题底部的空间加倍(底部边距在具有多个表单组的表单中提供正确的间距)。我同意我们的设计师认为双倍空间是错误的,它应该是与顶部相等的空间(就像在更简单的例子中)。我找到a good article试图解决这个问题的方法。但是,最好的"最后的选项(使用*:last-child)这是我喜欢的,因为表单不是容器中的最后一个元素,因为当窗口是时,操作按钮必须浮动在选择列表下面小。此外,我觉得这样的情况总是会出现。请注意,在这种情况下,当窗口很小并且按钮浮动在选择下方时,间距很好,因为窗体组上的边距提供了它们之间的间距。

此外,设计师说按钮应该与选择垂直对齐(使用bootstrap看起来更好,因为输入高度相同)。感觉没有办法实现对于此处元素的特定组合或此处出现的特定页面非常特定的内容。也就是说,我无法想象一个通用的最佳实践,可以使这样的事情恰到好处。

上述CSS太长,无法包含在这个已久的问题中,但请再次查看jsbin

重述问题:

同样,我不是在寻找能够修复这个特定示例的特定CSS。我想知道哪些最佳实践将允许我为分离的设计元素创建CSS,这些元素可以自由组合而不会经常遇到上述问题。

2 个答案:

答案 0 :(得分:1)

关于CSS中的可组合性

儿童的位置,尺寸应由父母负责

  

创建CSS的最佳实践是什么,以便进行各种设计   元素是分离的吗?

我已经对这个问题进行了相当多的考虑,我很高兴能够找到一位同事给你的文章"CSS is Not Composable"发送链接,这就是我发现这个问题的方法。

因为CSS本身有助于在全局空间中创建选择器,所以将解耦模块分隔成命名空间是明智的最佳实践

您可以选择在全局级别为定义通用组件类的基本样式,但依赖于命名空间类和/或ID,根据组件的位置将位置和维度样式应用于组件

不要将位置或尺寸样式直接应用于任何组件容器。所有其他样式都由其名称空间中的组件定义。

换句话说,子组件的位置和尺寸应该是父容器的责任。

正确:

/* A component namespace */
.my-component-A {
    padding: 10px;
    background-color: White;
}

/* A component styles its children as necessary */
.my-component-A > p {
    margin: 0 0 1em 0;
}

/* Position & dimension applied by parent (Page A) within its namespace */
.my-page-A .my-component-A { 
    float: left;
    margin: 0 10px 0 0;
}

/* Position & dimension applied by parent (Page B) within its namespace */
.my-page-B .my-component-A { 
    float: right;
    margin: 0 0 0 10px;
}

/* Position & dimension applied by parent (Component B) within its namespace */
.my-component-B .my-component-A {
    margin: 10px;
}

不正确:

/* Position & dimension applied by component within its own namespace */
.my-component-A {
    float: left;
    margin: 0 10px 0 0;
    padding: 10px;
    background-color: White;
}

/* Position & dimension now have to be overridden by parent */
.my-component-B .my-component-A {
    float: none;
    margin: 10px;
}

通过使用父组件或页面命名空间将位置和维度应用于子组件,您可以创建一个完全可组合的解耦模块系统。 并且因为组件永远不会更改自己的大小或位置,维护父组件或页面的开发人员可以保证他们可以控制子组件的布局。

如果您是一致且有意的,则每个组件都可以放入任何其他组件中。并且因为它没有定义自己的大小或位置,所以它会使用浏览器默认的位置和维度样式或全局重置提供的样式流入父容器。

作为维护页面或父组件的开发人员,您可以完全自由地修改该命名空间下所有内容的布局,而无需应用替代。如果您的团队参与了这个最佳实践,那么您不必担心对其他人维护的子组件所做的命名空间样式更改会破坏页面或父级的布局你维护的组件。

每个组件都是位置和维度无知的,完全依赖于其父组件或页面来根据需要定义其位置和大小。

在您的示例中,我们可以应用此主体来创建一个表单组组件,该组件可以放置在页面上,也可以放置在不同深度的其他组件中,同时放弃对布局的控制权到页面或父组件



/* Component Base / generic reset */
.component {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* Use generic page and component class to target all child components at once */
.pagewrapper > .component {
  margin: 20px;
}

/* Component: Panel */
.component.panel {
  /* No positional styles defined on component container */
  border: 1px solid Black;
}
.component.panel .panel-header {
  /* `display: table` is a great legacy & cross-browser solution for vertical alignment */
  display: table;
  position: relative;
  overflow: auto;
  border-bottom: 1px solid Gray;
}
.component.panel .panel-header > * {
  /* set all immediate children as table-cells to get vertical-alignment */
  display: table-cell;
  padding: 10px;
  table-layout: auto;
  border-collapse: collapse;
  vertical-align: middle;
}
.component.panel .panel-header > .actions {
  width: 1%;
}
.component.panel .panel-header h2 {
  margin: 0;
  font-size: 25px;
}
.component.panel .panel-content {
  /* Exclude bottom padding and apply margin-bottom to immediate children instead */
  padding: 10px 10px 0;
}
.component.panel .panel-content > * {
  /* All immediate children of the container get a bottom margin */
  margin: 0 0 10px;
}

/* Component: Form Group */
.component.form-group {
  /* No positional styles defined on component container */
  padding: 10px;
  background-color: Beige;
}
.component.form-group label {
  display: block;
}

/* Component position and dimension are namespaced to the parent */
.pagewrapper.home > .component.form-group {
  /* Use child selector to limit scope of namespaced components where a component may exist multiple times as descendants of the same ancestor. */
  /* You may override any other styles such as background-colors */
  background-color: Lavender;
}
.component.panel .panel-header .component.form-group {
  /* Positional style is always applied in the context of a parent container… */
  margin: -10px;
}
.component.panel .panel-content .component.form-group {
  /* …because it may need different positioning even within the same parent component. */
  margin: 0 -10px;
}
.component.panel .panel-content .component.form-group:first-child {
  /* Strategically use pseudo-class selectors… */
  margin: -10px -10px 0;
}
.component.panel .panel-content .component.form-group + * {
  /* …and sibling selectors (and other selectors as warranted). */
  margin-top: 10px;
}

<main class="pagewrapper home" id="my-page-A">

  <!-- Position and dimension defined by page…
       `.pagewrapper > .component` -->
  <section class="component panel" id="my-panel-A">
    <div class="panel-header">
      <h2>Title</h2>
      <div class="actions">
        <button>Add</button>
      </div>
    </div>
    <div class="panel-content">
      <p>Content</p>
    </div>
  </section>

  <section class="component panel" id="my-panel-B">
    <div class="panel-header">

      <!-- Position and dimension defined by parent component…
           `.component.panel .panel-header .component.form-group` -->
      <div class="component form-group" id="my-form-group-A">
        <label>A Label</label>
        <select>
          <option>Something</option>
        </select>
      </div>
      
      <div class="actions">
        <button>Add</button>
      </div>
    </div>
    <div class="panel-content">
      <p>Content</p>
      <p>More content</p>
    </div>
  </section>

  <section class="component panel" id="my-panel-C">
    <div class="panel-header">
      <h2>Title</h2>
      <div class="actions">
        <button>Add</button>
      </div>
    </div>
    <div class="panel-content">
      <p>Content</p>
      
      <!-- Position and dimension defined by parent component…
           `.component.panel .panel-content .component.form-group` -->
      <div class="component form-group" id="my-form-group-B">
        <label>A Label</label>
        <select>
          <option>Something</option>
        </select>
      </div>

      <p>More content</p>
    </div>
  </section>

  <!-- Position and dimension defined by namespaced page…
       `.pagewrapper.home > .component.form-group` -->
  <div class="component form-group" id="my-form-group-C">
    <label>A Label</label>
    <select>
      <option>Something</option>
    </select>
  </div>

</main>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

CSS并不意味着是分开的。它旨在具有父级到子级联效果,并且在应用良好时可以非常强大,可靠且有弹性。 OOCSS接近你想要的但不完全。使用OOCSS的目的是将公共属性放在一个可以在许多实例中重用的类中。

我认为您可能想要做的是研究Web Components and the use of Shadow DOM。它允许您创建小部件,以便它们具有与主页面DOM分开的自己的样式,并且可以进一步减少不需要的样式的机会。

编辑:我知道你说你正在寻找最佳实践而不是解决方案。但是,我觉得我应尽可能提供一些可能的代码示例&#34;最佳实践&#34;解决您发布的情况。为了更好地控制你,你应该使用更具体的css选择器,因为它们在渲染时会被赋予更高的重要性。作为一个&#34;最佳实践&#34;为了获得最大程度的控制,您应该使用具有唯一ID的容器,您可以使用这些容器来定位html中具有更大specificity部分(即。#mainContent#sideBar,{{1}等等)。然后,您可以将它们与各种CSS3 Selectors配对,以提高特异性并实现更好的控制。

TLDR:一般CSS最佳实践

  1. 从基本样式开始(OOCSS对此有利)
  2. 为您的重要容器元素添加唯一ID
  3. 具体使用容器ID,类,CSS3选择器等 这些容器中的目标元素和覆盖基本样式 需要
  4. 另请注意,您放置规则的位置很重要(样式表顶部,样式表底部,内联等)。
  5. &#13;
    &#13;
    #myWidgetName
    &#13;
    /* ----- YOUR ORIGINAL STYLES -----*/
    
    /* General form styles */
    .form-group {/* like bootstrap, need space between form inputs */margin-bottom: 10px;}
    .form-group label {display: block;}
    
    /* Panel styles */
    .panel {border: 1px solid black; background-color: white; margin: 20px;}
    .panel-header {padding: 10px; border-bottom: 1px solid grey;}
    .panel-header h2 {font-size: 25px; margin: 0px; display: block; float: left;}
    .panel-header .form-group {float: left;}
    .panel-header .actions {float: right; margin-top: 2px; /* nudge down to try and line up with title */}
    .panel-content {padding: 10px;}
    
    /* generic clear fix */
    .clearfix:after {content: ""; display: table; clear: both;}
    
    
    
    
    /* ----- SUGGESTED SOLUTION STYLES -----*/
    .panel-header {position:relative;/* will assist with button alignment */}
    .panel-header > *:nth-child(n) {
        margin-bottom: 0;/* zero out the bottom margin of all direct children of ".panel-header" */
    }
    @media (min-width: 768px) {
    .panel-header .actions { /* now that the bottom margins are set this will position the button to be aligned to the bottom of the container */
        bottom: 10px;/*matches .panel-header margin*/
        float: none;
        position: absolute;
        right: 10px;/*matches .panel-header margin*/
    }
    }
    &#13;
    &#13;
    &#13;