FDS Concepts

This page discusses various concepts that are important to understand when starting to build UI components with FDS.

Components and System

FDS as a library is divided into two distinctive parts: components.js and system.js. Both files serve as separate entry points into the FDS library (and thus are located in the root folder of the library).

Components are intended to be (mostly) usable out-of-the-box. Most components are quite small, like a Button or a CalendarInput. More elaborate components, such as specific modals or forms as a whole, are developed as separate feature packages, using these 'simple' FDS components.

System is intended to be a toolbox of utilities and helper functions to create components, including a set of reusable styles / mixins if you will(just a JavaScript function that returns some inline style rules based on some parameters). These can be composed as well, as is explained in more detail in the next chapter.

Styling components with inline styles

The main difference between inline styles and ‘normal CSS’ is that with inline styles, you don’t write any selectors. Instead you apply the css directly to the element which you want to style. In JSX, you can do this very easily by spreading the result of Glamor’s css() method as an attribute of the DOM element you want to style.

Eg. <div { ...css({ flex: 1 }) }>

Every invocation of css() generates a unique class (and matching data- attribute) selector. However, Glamor is smart enough to reuse the same classes (ids) if the CSS rules are the same. Eg. calling css({ flex: 1 }) twice in the same application will result in a single selector instead of 2 selectors with the same rules.

Another feature of the css() method is that it can merge previously defined pieces of css into the same generated selector.

Eg. there is a utility function called flex() which you give a direction as a parameter and it generates two rules: flex(‘horizontal’) would generate a selector with two rules: display: flex; and flex-direction: row;. And then you want to use this flex() function and combine it with other css rules.

In Glamor, you would do something like: css(flex(‘horizontal’), { flex: 1 });

This has one major disadvantage, it creates a new unique class with three rules in it: display: flex; flex-direction: row; flex: 1; It didn’t reuse the existing class with the flex and flexDirection rules.

In FDS, we’ve added a utility function called applyCss() which accepts a single rule or an array of rules and does not merge the given css rules into a single class, instead it calls Glamor’s css() function for each set of rules in the given array. (The css function is smart enough to detect if it’s given the result of an earlier call to css.)

So the previous example using applyCss would become:

applyCss([ flex(‘horizontal’), { flex: 1 } ])

Which would result in two unique classes. One for the flex(‘horizontal’) part, possibly reusing earlier invocations of flex(‘horizontal’), and one for the { flex: 1 } part. (And potentially reusing this again later.)

The amount of CSS that is generated when using FDS' applyCss() vs Glamor's css() is considerably less. This means there is less work at runtime to add all the different css class definitions to the style tag in the head of the page.

There is one thing to keep in mind: applyCss() cannot guarantee anything about the order of the properties between different pieces of css. Eg. there is no way to definitively say which background-color would ‘win’ here:

applyCss([ { backgroundColor: blue }, { backgroundColor: red } ])

Generally it’s a good idea to exclude or modify the desired style rule, for example when using a mixin. However if this isn’t possible and you need to override a certain style rule, make sure to use Glamor’s css() function. Because it respects the order in which you define the separate sets of rules when merging them together. applyCss() generates (or reuses) unique class selectors for each set or rules and applies them both to an element if used like this:

<div { ...applyCss([ … ]) }>.

FDS also provides layout container components to easily apply styling and compose custom layouts. There is a Block and Flex component corresponding to the css display: block (and inline-block) + display: flex (and inline-flex) respectively. Sometimes you need an extra DOM element somewhere to make the styling work, but you might not want to make it into a separate React Component just to apply some styles. In that case, you can use Flex or Block. Both components accept some common css rules directly as component props, and both have a prop called applyCss which works exactly the same as the utility function mentioned earlier.

On keyboard behavior

In many cases it’s desirable to support keyboard behavior in order to complement the user's’ experience. In order to do this we use key events. Generally it’s recommended to use the keydown event, as this fires immediately when pressing the button and allows us to prevent any default browser behavior such as scrolling using event.preventDefault().

Keyboard behavior is implemented in a considerable amount of the components provided by FDS.

To implement your own, start out by simply using React's onKeyDown prop directly. Some FDS components like Modal also expose this property.

Focus management

Most components in FDS that support user interaction can be focused by the user as well. Focusing a component in FDS means in most cases it will be visualized with a blue, slightly glowing, focus border.

It is also possible to focus a component on a certain moment in time, for example when a modal or sidebar is mounted.

All the form fields and all other components that have focus styling, will have a focus method on the component. When the focus method is executed, the component it self is focused or a part of the component that seems most logical to focus at that time. For example when calling focus on MultipleTextInput the first chip is focused except when there aren't any chips, then the TextInput is focused.

To execute this focus method, you need to get a reference to the component first. This is done by implementing the React ref prop as shown below.

JavaScript

import React, { useEffect, useRef, useState } from 'react';


import { Modal, ModalBody, ModalHeader, TextInput } from 'fds/components';

function ExampleModal() {
	const textInputRef = useRef(null);	

	const [exampleValue, setExampleValue] = useState('');

	useEffect(() => {
		textInputRef.current.focus();
	}, []);

	return (
		<Modal size="m">
			<ModalHeader title="Example modal" />

            <ModalBody>
				<TextInput
					onChange={setExampleValue}
					ref={textInputRef}
					value={exampleValue}
				/>
			</ModalBody>
		</Modal>
	);
}

Ref vs onRef

React has a special prop called ref that you can pass to components as shown previously, or on DOM nodes. When placing a ref on a component, you get a reference to the instance of the component. If you place a ref prop on a DOM node, you get a reference to that DOM node. For some (FDS) components, you might need a reference to the DOM node of that component (to measure it for example). In that case, the (FDS) component has a onRef prop, which works similar to React's ref prop, but instead, you always get a DOM node back (usually the root DOM node of the component on which you placed the onRef callback prop).

Note: React also has a built-in solution to forward a ref on a component instance to a DOM node, called forwardRef(). This was introduced after FDS had already introduced onRef in its APIs. Feel free to use React's solution for your own components.