Are there any useful techniques for reducing the repetition of constants in a CSS file?
(For example, a bunch of different selectors which should all apply the same colour, or the same font size)?
Recently, variables have been added to the official CSS specs.
Variables allow you to so something like this :
body, html {
margin: 0;
height: 100%;
}
.theme-default {
--page-background-color: #cec;
--page-color: #333;
--button-border-width: 1px;
--button-border-color: #333;
--button-background-color: #f55;
--button-color: #fff;
--gutter-width: 1em;
float: left;
height: 100%;
width: 100%;
background-color: var(--page-background-color);
color: var(--page-color);
}
button {
background-color: var(--button-background-color);
color: var(--button-color);
border-color: var(--button-border-color);
border-width: var(--button-border-width);
}
.pad-box {
padding: var(--gutter-width);
}
<div class="theme-default">
<div class="pad-box">
<p>
This is a test
</p>
<button>
Themed button
</button>
</div>
</div>
Unfortunately, browser support is still very poor. According to CanIUse, the only browsers that support this feature today (march 9th, 2016), are Firefox 43+, Chrome 49+, Safari 9.1+ and iOS Safari 9.3+ :
Alternatives :
Until CSS variables are widely supported, you could consider using a CSS pre-processor language like Less or Sass.
CSS pre-processors wouldn't just allow you to use variables, but pretty much allow you to do anything you can do with a programming language.
For example, in Sass, you could create a function like this :
#function exponent($base, $exponent) {
$value: $base;
#if $exponent > 1 {
#for $i from 2 through $exponent {
$value: $value * $base;
}
}
#if $exponent < 1 {
#for $i from 0 through -$exponent {
$value: $value / $base;
}
}
#return $value;
}
Elements can belong to more than one class, so you can do something like this:
.DefaultBackColor
{
background-color: #123456;
}
.SomeOtherStyle
{
//other stuff here
}
.DefaultForeColor
{
color:#654321;
}
And then in the content portion somewhere:
<div class="DefaultBackColor SomeOtherStyle DefaultForeColor">Your content</div>
The weaknesses here are that it gets pretty wordy in the body and you're unlikely to be able to get it down to listing a color only once. But you might be able to do it only two or three times and you can group those colors together, perhaps in their own sheet. Now when you want to change the color scheme they're all together and the change is pretty simple.
But, yeah, my biggest complain with CSS is the inability to define your own constants.
You should comma seperate each id or class for example:
h1,h2 {
color: #fff;
}
You can use global variables to avoid duplicacy.
p{
background-color: #ccc;
}
h1{
background-color: #ccc;
}
Here, you can initialize a global variable in :root pseudo class selector. :root is top level of the DOM.
:root{
--main--color: #ccc;
}
p{
background-color: var(--main-color);
}
h1{
background-color: var(--main-color);
}
NOTE: This is an experimental technology
Because this technology's specification has not stabilized, check the compatibility table for the proper prefixes to use in various browsers. Also note that the syntax and behavior of an experimental technology is subject to change in future versions of browsers as the spec changes. More Info here
However, you can always use the Syntactically Awesome Style Sheets i.e.
In case Sass, you have to use $variable_name at the top to initialize the global variable.
$base : #ccc;
p{
background-color: $base;
}
h1{
background-color: $base;
}
You can use dynamic css frameworks like less.
Personally, I just use comma-separed selector, but there some solution for writing css programmatically. Maybe this is a little overkill for you simpler needs, but take a look at CleverCSS (Python)
Try Global variables to avoid duplicate coding
h1 {
color: red;
}
p {
font-weight: bold;
}
Or you can create different classes
.deflt-color {
color: green;
}
.dflt-nrml-font {
font-size: 12px;
}
.dflt-header-font {
font-size: 18px;
}
As far as I know, without programmatically generating the CSS file, there's no way to, say, define your favorite shade of blue (#E0EAF1) in one and only one spot.
You could pretty easily write a computer program to generate the file. Execute a simple find-and-replace operation and then save as a .css file.
Go from this source.css…
h1,h2 {
color: %%YOURFAVORITECOLOR%%;
}
div.something {
border-color: %%YOURFAVORITECOLOR%%;
}
to this target.css…
h1,h2 {
color: #E0EAF1;
}
div.something {
border-color: #E0EAF1;
}
with code like this… (VB.NET)
Dim CssText As String = System.IO.File.ReadAllText("C:\source.css")
CssText = CssText.Replace("%%YOURFAVORITECOLOR%%", "#E0EAF1")
System.IO.File.WriteAllText("C:\target.css", CssText)
You can use multiple inheritance in your html elements (e.g. <div class="one two">) but I'm not aware of a way of having constants in the CSS files themselves.
This link (the first found when googling your question) seems to have a fairly indepth look at the issue:
http://icant.co.uk/articles/cssconstants/
CSS Variables, if it ever becomes implemented in all major browsers, may one day resolve this issue.
Until then, you'll either have to copy and paste, or use a preprocessor of whatever sort, like others have suggested (typically using server-sider scripting).
:root {
--primary-color: red;
}
p {
color: var(--primary-color);
}
<p> some red text </p>
You can change color by JS
var styles = getComputedStyle(document.documentElement);
var value = String(styles.getPropertyValue('--primary-color')).trim();
document.documentElement.style.setProperty('--primary-color', 'blue');
Related
I am using polymer 3 and lit-element(2.2.1). The version of mwc-textfield is 0.13.0. I have read the documentations related to this version.In this documentation, I have found that we can add mixin for height. I had tried several ways but did not succeed. May be the syntax I am using is wrong. I want to decrease the height of my text field. This is my text field
<mwc-textfield id="textBox" .type="text" .value=${this.title} .placeholder='' minLength="10" maxLength="256"></mwc-textfield>
and my css
#textBox{
text-transform: none;
--mdc-theme-primary: transparent;
--mdc-text-field-fill-color: #fff;
--mdc-text-field-hover-line-color: #f5f5f5;
--mwc-text-width: 100%;
width:100%;
}
The default css applied is
:host(:not([disabled])) .mdc-text-field:not(.mdc-text-field--outlined) {
background-color: transparent;
}
.mdc-text-field:not(.mdc-text-field--disabled) {
background-color: rgb(245, 245, 245);
}
.mdc-text-field {
display: flex;
width: 100%;
}
.mdc-text-field {
height: 56px;
display: inline-flex;
position: relative;
box-sizing: border-box;
will-change: opacity, transform, color;
border-radius: 4px 4px 0px 0px;
overflow: hidden;
}
.mdc-text-field {
--mdc-ripple-fg-size: 0;
--mdc-ripple-left: 0;
--mdc-ripple-top: 0;
--mdc-ripple-fg-scale: 1;
--mdc-ripple-fg-translate-end: 0;
--mdc-ripple-fg-translate-start: 0;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
user agent stylesheet
label {
cursor: default;
}
<style>
#textfield {
width: var(--text-field-width,80%);
height: 100%;
position: absolute;
left: 0;
top: -12px;
text-transform: capitalize;
--mwc-text-width: 100%;
}
<style>
mwc-textfield {
--mdc-theme-primary: transparent;
--mdc-text-field-ink-color: black;
--mdc-text-field-fill-color: transparent;
--mdc-text-field-disabled-fill-color: transparent;
}
The default height applied to the text field is 56px. What I have tried is
#textbox.mdc-text-field--height{
height:45px;
}
and
#textbox.mdc-text-field--height('45px');
and also added mixin in the node modules file as height:var(--mdc-text-field-height,56px);
and used in css as
#textBox{
--mdc-text-field-height:45px;
}
Any help would be greatly appreciated! Thanks in advance.
Material design components vs Material web components
I have read the documentations related to this version. In this documentation, I have found that we can add mixin for height.
The first thing to note here is that there are two different libraries of material components: the one you are referring to is MDC (Material Design Components, distributed on npm as #material/<component>) which is a SASS+JS implementation of Material components. The other one is MWC (Material Web Components, distributed as #material/mwc-<component>), a collection of actual WebComponents based on the former library. So keep in mind that the documentation refers to the MDC counterpart of the MWC component you're actually using (<mwc-textfield>).
Styling from the outside
What you're trying to do here
#textbox.mdc-text-field--height {
height: 45px;
}
doesn't work mainly because selecting inside a custom element's shadow root is not possible (at least, not anymore); also, the element responsible for the height is the <label>, whose class is .mdc-text-field.
The querySelector way
The quickest way to change the height that comes to my mind is this:
import { LitElement, html, property, customElement, css, query } from 'lit-element';
import '#material/mwc-textfield';
#customElement('my-component')
export class MyComponent extends LitElement {
// Select the text field
#query('mwc-textfield') textField;
async firstUpdated() {
// Wait for its dom to be ready
await this.textField.updateComplete;
// Programmatically select the label
// and change the height
this.textField
.shadowRoot
.querySelector('.mdc-text-field')
.style
.height = '45px';
}
render() {
return html`<mwc-textfield></mwc-textfield>`;
}
}
however I would really not recommend it: performance and elegance aside, it'll probably break some of mwc-textfield features such as the floating label.
The extension way
You can also enforce the height by extending TextField and overriding the styles:
import {LitElement, html, customElement, css} from 'lit-element';
import {TextField} from '#material/mwc-textfield/mwc-textfield';
#customElement('my-textfield')
export class MyTextfield extends TextField {
static styles = [TextField.styles, css`
.mdc-text-field {
height: 45px;
}
`];
}
// Then use <my-textfield> instead of <mwc-textfield>
but again, like the above, use at your own risk...
Using the mixin
I guess for now the only way of using the height mixin is building a customised version of TextField which more or less goes like this:
clone the mwc repo (yeah, it's a monorepo so you get all the other components as well, but I'm pretty sure you can delete all the ones not imported by mwc-textfield)
npm install
in packages/mwc-textfield/src/mwc-textfield.scss use the mixin:
#include mixins.height(45px);
probably around here
npm run build
copy the mwc-textfield folder and paste it in your project (delete the source files, npm pack may be handy for this), then change the imports from #material/mwc-textfield to ./path/to/custom-textfield
Certainly too much work for changing a height... The good news is MWC is still in development and it cannot be excluded that they'll add a CSS custom property or some other way to customise the height. Also, the new density concepts are being implemented in MWC (sadly not yet in TextField), which could be just what you need.
There is also an open issue about this, let's see what they say
I am trying to closely stick to the BEM methodology and I am frequently running into this issue which I feel it is time to ask for a solution (or opinion) to.
Consider I have a human body; this human body is light by default and therefore, it's arms are also light. My pseudo-code in CSS may look like the below:
.human {
background-color: white;
}
.human__arm {
background-color: white;
}
Now we want to add a class for humans who are dark. We can create a modifier for this:
.human--dark {
background-color: black;
}
The problem I am facing is that by now, the human's arm is still white. We can make them black in two ways that I know of:
Solution A
.human--dark {
background-color: black;
}
.human--dark .human__arm {
background-color: black;
}
This solution breaks the BEM methodology by adding specificity to the CSS. However, I feel that this solution is more portable where you are sure to only modify the human and assume that all of its elements will also adapt to the changes of the parent.
Solution B
.human--dark {
background-color: black;
}
.human__arm--dark {
background-color: black;
}
I like to think that a 'Block' is a reusable component in BEM. If the human has various other body parts that also need turning white, it seems less maintainable to modify all of the blocks elements in order to achieve this.
While background-color: inherit; may look like a solution to this case, in a real-world application we may have a dark background that contains elements that require light text.
How would we modify the light text to become dark when its parent block becomes light?
The first solution is the better one assuming that a dark human may not necessarily have a white arm.
The idea behind BEM syntax is to allow for composability. Writing your selector that way defeats this purpose.
A better way to define these selectors is:
.human {} /* block */
.human--dark {} /* modified block */
.human--arm {} /* block element */
These can be altogether composed this way in markup:
<div class="human human--dark">
<div class="human__arm">
Human
</div>
</div>
For your second question, you may want to take advantage of a CSS preprocessor to compute the inverse of the background color for the text color.
In Sass, it's done this way using a custom mixin:
#mixin duotone ($color) {
background-color: $color;
color: invert($color);
}
.human {
#include duotone(white);
}
.human--dark {
#include duotone(black);
}
.human__arm {
}
In order to modify child elements via parent modifier
we should nest the element selector like below:
Block with an element:
.human {
background-color: white;
}
.human__arm {
background-color: white;
}
Block Modifier modifying the child element:
.human--dark {
background-color: black;
}
.human--dark .human__arm {
background-color: black;
}
If we want to achieve the same in SCSS, we can use the following approach:
.human {
background-color: white;
&__arm {
background-color: white;
}
&--dark { // block modifier
background-color: black;
.human { // block
&__arm {
background-color: black;
}
}
}
}
If you're using scss you can use interpolation to avoid duplication
.human {
$c: &;
background-color: white;
&__arm {
background-color: white;
}
&--dark {
background-color: black;
#{$c}__arm {
background-color: black;
}
}
}
Also, this may not be relevant to the question, but using background-color: inherit on the arms would mean you wouldn't have to change any css for the arms as it would inherit from the parent.
Consider this:
.voffset1 { margin-top: 1px; }
.voffset2 { margin-top: 2px; }
.voffset3 { margin-top: 3px; }
......
.voffset100 { margin-top: 100px; }
Is it possible to turn this into a function in CSS or CSS3
so I don't have to manually have 100 css classes, but
just have 1 css 'function' for it?
like below:
.voffset:(n) {margin-top : (n)px;}
if not, can we do better than writing 100 lines?
I prefer not using scss or any variants.
Sure.
<div style="margin-top:1px">
When your CSS is so obviously redundant, just use inline styles.
Or redesign your code to actually use classes properly.
I'm trying to create a HTML widget:
HTML:
<div>
<h1 class="title" data-bind="title">Title</h1>
<div>
<h1 id = "dc1" class="dc">DC1</h1>
</div>
<div>
<h1 id = "dc2" class="dc">DC2</h1>
</div>
<p class="updated-at" data-bind="updatedAtMessage"></p>
</div>
And I need to be able to set the background color of the id="dc1" and id="dc2" elements dynamically in CoffeeScript. I plan to do this by adding a class with a background color setting:
SCSS:
&.up {
background-color: green;
}
&.down {
background-color: red;
}
.dc {
background-color: orange;
font-size: 30px;
float: left;
width: 50%;
}
So far I have managed to set the whole widget background but not the child elements mentioned above:
I have been using:
CoffeeScript:
$(#node).removeClass('up down')
$('#dc1').removeClass('up down')
$('#dc2').removeClass('up down')
$(#node).addClass('down')
$('#dc1').addClass('down')
$('#dc2').addClass('up')
Note ultimately I will add the classes depending on some data rather than hard coding them to 'up' or 'down' in the coffeescript.
But nothing happends.. Am I getting selecting the id="dc#" elements correctly?
If it helps with context I'm doing this for Dashing
Your SCSS doesn't make sense so I'd guess that your missing an error from the SCSS-to-CSS conversion. An & in SCSS is a reference to the parent selector:
& will be replaced with the parent selector as it appears in the CSS
so have &.up at the top level makes no sense and should generate an error. If we fix the SCSS so that .up and .down apply only to .dc:
.dc {
/* ... */
&.up {
background-color: green;
}
&.down {
background-color: red;
}
}
then everything seems to work just fine.
Demo: http://jsfiddle.net/ambiguous/9y9uywm9/
You can use Sassmeister (and other similar online tools) to see what SCSS thinks of your original SCSS.
Suppose I have the following css rule:
.blah { Rules }
and I use it as such
<div class="blah">
Now suppose, somewhere in my doc I need to add margin-bottom: 10px to one of these divs with class name blah so I can make it as specific as possible,
Should I declare it as
.blah.mar-bot-blah { margin-bottom: 10px; }
or
.blah .mar-bot-blah { margin-bottom: 10px; }
to use it as <div class="blah mar-bot-blah">
Declare it as:
.blah.mar-bot-blah { margin-bottom: 10px; }
It will match elements that have both the classes .mar-bot-blah and .blah.
If you want to make it really specific, I would use your first solution.
(Warning: IE6 Can't handle this!)
In most cases it should suffice to just use
.mar-bot-blah { margin-bottom: 10px; }
so you can re-use this setting in other classes as well.