CSS Style - js-lib-com/wood GitHub Wiki

WOOD recognizes as good practice and uses W3C separation of concerns: HTML files for layout description and CSS files for styles. Anyway, WOOD goes a step further and divide styles into layout styles - rules for component layout dimension and position, and aspect styles that deals with application visual aspect also known as theme.

Layout styles are part of component core file whereas aspect styles are stored on project theme directory. It is not allowed to use aspect styles on component. This clear distinction assure consistent look and avoid style rules specificity misuse and rule conflicts by overwriting.

To sum up, in a WOOD project we can find CSS style files in one of the two places:

To facilitate third party libraries integration styles can be included into generated pages directly from third party servers using full URL. Also is possible to download third party style files and include into WOOD project into arbitrary directory, perhaps lib. For that, third party styles should be declared into project or component descriptors.

Syntax

Style files syntax is almost standard CSS, including variables and calc function.

It is deemed almost because of media files references; media file path is not known till build is performed - which depends on target site directories structure, and is not possible to include media files by file path. For a discussion about references see References.

In sample code @image/background is a reference to an image file located into component directory or global assets. By convention media file reference is the file name but without extension.

  • page/index/index.css
body {
	/* media file reference */
	background-image: url(@image/background);
}

Build tool replaces media file references with file path, relative to current style file location into target site.

  • site/style/page_index.css
body {
	/* media file path relative to style file location */
	background-image: url(../media/background.png);
}

There is also a recommended selectors codding:

For component layout style always uses namespace and child selector combinator. In sample, .index CSS class is used as namespace and combinator is child selector, '>' character; remember that using space, combinator becomes descendant selector and there is the risk to extends rule scope over child components. Also, if selector contains more than two simple selectors, combinator child selector is strictly needed only for the first child.

  • page/index/index.css
.index > .header { ... }

.index > .header span { ... }

For theme style always uses element tag name. It is acceptable to decorate element tag with a CSS class.

  • theme/button.css
button { ... }

button.danger { ... }

Component Styles

Regarding styles, developer need only to create components style files and to import / adjust / create project theme. Component style file describe layout rules whereas project theme describe visuals.

When aggregate components, build tool takes care to also process related style files automatically, including merging media queries. Anyway, if working with third party libraries need to explicitly declare third party styles into component descriptor.

Lets consider a page component page/index with a child component compo/list-view. Page component has a layout file page/index/index.htm and a style file page/index/index.css.

  • page/index/index.htm
<body class="index" xmlns:wood="js-lib.com/wood">
	<div wood:compo="compo/list-view"></div>
</body>

Page component layout style describe dimensions and positions for its child elements. It is not allowed to use aspect rules into page component style file.

  • page/index/index.css
.index > element { ... }

Child component compo/list-view has a layout file compo/list-view/list-view.htm and a style file compo/list-view/list-view.css.

  • compo/list-view/list-view.htm
<div class="list-view"> ... </div>

Child component layout style describe dimensions and positions for its child elements. It is not allowed to use aspect rules into component style file.

  • compo/list-view/list-view.css
.list-view > element { ... }

Project theme styles contains only aspect rules related to fonts, colors, opacity, round border, etc. It is not allowed to use layout rules into theme styles, e.g. width or height.

  • theme/page.css
body { ... }
  • theme/list-view.css
list-view { ... }

Into generated page - site/index.htm, beside aggregating HTML layout, there are external resource link elements for style file inclusion. See styles inclusion order. Style links are inferred automatically by build tool based on component relations.

  • site/index.htm
<HTML>
	<HEAD>
		<!-- theme styles, in no particular order -->
		<LINK href="style/theme_page.css" rel="stylesheet" type="text/css" />
		<LINK href="style/theme_list-view.css" rel="stylesheet" type="text/css" />
		<!-- layout styles, child before parent, recursively -->
		<LINK href="style/compo_list-view.css" rel="stylesheet" type="text/css" />
		<LINK href="style/page_index.css" rel="stylesheet" type="text/css" />
	</HEAD>
	<BODY>
		<DIV class="list-view"></DIV>
	</BODY>
</HTML>

Project Theme

Component styles deal only with layout properties like child elements dimensions and positions. All other rules, more or less related to visual aspect, are grouped into separated style files, global per all site pages, named project theme. These theme files are stored in a dedicated directory, usually named theme, configurable on project descriptor.

For related applications, that uses similar look and feel, theme styles can be reused. It is possible to import the project theme. After import there is the option to customize theme styles or to always rely on import updates.

Usually theme styles apply to element tags. A theme style crosscuts entire page and is not confined by container boundaries. For example, all page's buttons look the same not mater the component it is part from.

  • theme/button.css
button {
	background-color: var(--button-bg-color);
	color: var(--button-color);
}

Also, there is the custom to decorate element tag with a CSS class to signal specific functionality.

  • theme/button.css
button.accent {
	background-color: var(--accent-bg-color);
}

button.danger {
	background-color: var(--danger-bg-color);
}

If there are more theme files, is not allowed to split a component theme on multiple files; in above example, all button related theme styles are in the same file theme/button.css. The rule is to not create dependencies between theme files. This way theme files can be included into generated pages in no particular order, see styles inclusion order.

On project theme directory there are also three auxiliary (optional) files, included in all site pages in listed order:

File Name Description
var.css CSS Variables CSS variables assigned to :root pseudo element
default.css Default Styles Also known as styles reset, contain initial values for all graphical elements
fx.css CSS Animation Rules for CSS animations

CSS Variables

CSS variables, also knows as custom properties, are supported by component layout styles. Usually variables are declared on var.css file from project theme directory on :root pseudo-class. Its probably a good idea to keep variables grouped by types.

  • theme/var.css
/** dimensions */
:root {
	--bar-height: 3rem;
}

/** font sizes */
:root {
	--font-size: 15px;
}

/** colors */
:root {
	--danger-bg-color: #d94d48;
}

CSS variables can be referred only from CSS style files, with standard syntax var(--variable) and is text replaced by browser.

  • template/page/page.css
.page > .top-menu {
	height: var(--bar-height);
}

Custom Font

Custom font files are stored also on project theme directory. For example Segoe fonts file downloaded from open source fonts is stored into theme/segoe.woff file. This font file is a resource that can be references from a style file using at-meta reference.

Also we have a theme/font.css style file that declare fonts family using @font/segoe font reference. In this case segoe reference name matches base name for theme/segoe.woff font file.

  • theme/font.css
@font-face {
	font-family: 'Segoe';
	font-style: normal;
	font-weight: normal;
	src: local('Segoe'), url('@font/segoe') format('woff');
}

After built tool processing font reference is replace with font file path, relative to style file location.

  • site/style/theme_font.css
@font-face {
	...
	src: local('Segoe'), url('theme_segoe.woff') format('woff');
}

Third Party Styles

When working with third party libraries that provide style files need to explicitly declare them. This is true for both third party styles linked from third party servers or downloaded into project.

Considering example Component Styles, developer needs to create component descriptor file and declare third party styles. It is also possible to declare global third party styles, per project. These global third party styles will be included in all site pages.

  • page/index/index.xml
<page>
	<!-- third party styles downloaded into lib directory -->
	<link href="lib/toolbar.css"></link>
	<!-- third party styles loaded from third party server -->
	<link href="https://bootstrap.com/bootstrap.css"></link>
</page>

Into generated page third party styles are included on top of all other styles, in the order from descriptor. See styles inclusion order for details.

  • site/index.htm
<HTML>
	<HEAD>
		<!-- third party styles in the order from link descriptors -->
		<LINK href="style/lib_toolbar.css" rel="stylesheet" type="text/css" />
		<LINK href="https://bootstrap.com/bootstrap.css" rel="stylesheet" type="text/css" />
		<!-- the other styles -->
		...
	</HEAD>
	...
</HTML>

Media Queries

Media queries allow for different layout styles based on device properties, for example screen width and height. For that, a block of CSS rules are controlled by an expression like @media screen and (min-width:1200px); browser will process CSS rules only if device fulfill media query, in example screen wider than 1200px. For details see Media Queries.

WOOD allows to split media queries styles on style file variants with custom alias, for example page/index/index_portrait.css. In order to use a media query file variant it should be declared on project descriptor, media-query element; alias attribute is the file variant and expression attribute is media feature always related to screen media type.

<project>
	...
	<media-queries>
		<media-query alias="portrait" expression="orientation: portrait" />
		<media-query alias="lgd" expression="min-width: 1200px" />
		<media-query alias="mdd" expression="max-width: 992px" />
		<media-query alias="smd" expression="max-width: 768px" />
		<media-query alias="xsd" expression="max-width: 560px" />
		<media-query alias="h700" expression="max-height: 700px" />
	</media-queries>
	...
</project>

Here is a page template template/page with base style template/page/page.css and two media query variants: template/page/page_lgd.css and template/page/page_smd.css.

  • template/page/page.css
.page > .header {
	width: 1000px;
}
  • template/page/page_lgd.css
.page > .header {
	width: 600px;
}
  • template/page/page_smd.css
.page > .header {
	width: 400px;
}

Resulting CSS file will include all media query variants in media query blocks, using expression from media queries definition from project descriptor.

.page > .header {
	width: 1000px;
}

@media screen and (min-width:1200px) {
.page > .header {
	width: 600px;
}
}

@media screen and (max-width:768px) {
.page > .header {
	width: 400px;
}
}

Page Inclusion Order

While aggregating components into site pages build tool takes care to collect style files, usually in a separated directory but this depends on target site directories structure. Also, on generated pages header uses external resource link to include related style files.

When browser loads the page, CSS files order mater because CSS styles allow for rule overriding. The first rule of thumb when processing styles is to always include parent component styles after child styles. This is to avoid children overriding parent styles by accident.

The second WOOD rule is to include project theme styles before component layout styles. This one is to avoiding overriding layout styles by global theme styles.

And rule number three is to include third party styles first, in order to avoid overriding project own styles. This applies to both styles loaded from third party servers via full URL or style files downloaded and included into project.

Here is summarized styles inclusion order:

  1. third party style files, defined by project descriptor
  2. third party style files, defined by page component descriptor
  3. CSS variables var.css from project themes, if any
  4. default styles, aka styles reset, default.css from project themes, if any
  5. CSS animations fx.css from project themes, if any
  6. the rest of the theme styles - theme styles are in no particular order since they are independent of each other
  7. component layout styles - first include child styles then the parent ones

CSS variables, default styles and CSS animations files are optional.

Style Namespace

CSS styling is intrinsically complex. WOOD component based approach breaks styles complexity in manageable units but is prone to name collision and style overriding. Now, overriding can be on purpose but also by accident and for complex sites can be hard to detect.

In order to avoid accidental overriding WOOD recommend using namespace for component CSS styles. A namespace is simple an unique name used as CSS class that prefixes all style rules related to component. Name uniqueness should be ensured by developer.

Here is a page component page/index. It has CSS namespace index declared by CSS class on component root element. Page component has a child element with a class header.

  • page/index/index.htm
<!-- page component layout -->
<body class="index" xmlns:wood="js-lib.com/wood">
	<div class="header"></div>
	<!-- child component inclusion -->
	<div wood:compo="lib/list-view"></div>
	<div class="footer"></div>
</body>

Page component style used CSS class to prefix all rules sets.

  • page/index/index.css
/* page component style */
.index .header {
	width: 100%;
}

Here is the child component lib/list-view used by above page component. It happed to also have a child element with the class header.

  • lib/list-view/list-view.htm
<!-- child component layout -->
<div class="list-view">
	<div class="header"></div>
</div>

Child component has its own namespace and a different value for its header element width.

  • lib/list-view/list-view.css
/* child component style */
.list-view .header {
	width: 50%;
}

Because WOOD includes child style before parent - see inclusion order, resulting style, after browser processing, may look like:

.list-view .header {
	width: 50%;
}
.index .header {
	width: 100%;
}    

And here we face a problem: list view header width is overridden and becomes 100%, although list view header should be opaque for parent page. Solution is to use child selector combinator, that is, .index > .header instead of .index .header.

  • page/index/index.css
/* page component style */
.index > .header {
	width: 100%;
}
  • lib/list-view/list-view.css
/* child component style */
.list-view > .header {
	width: 50%;
}

This way parent header rule does not extends to include child header.

An alternative solution is shadow DOM provided by Web Component.


Last updated 9/8/2022.

⚠️ **GitHub.com Fallback** ⚠️