This document outlines the design principles followed by components in UI Library that makes them easily customizable. Implementation guidelines are discussed in detail below.
- Simple & Idiomatic
- Flexible & Forgiving
- Composable
- Consistent
- Responsive
- Observable
We should strive to keep component usage as simple as possible. Component names, props and styling should be familiar to the end user.
For example, VideoTile
instead of StreamTile
or MediaTile
is a more descriptive name.
A UI component can’t enforce where and how it should be used. Each component should expose capabilities that allow it to be styled for any user experience.
For example, a ControlBar
component should allow a user to render it horizontally or vertically and insert custom control buttons.
An end user should be able to decompose a component to its smallest element and be able to compose new components using those elements. Instead of being an "all-or-nothing" solution, our components should allow partial usage.
Given that the UI Library is built on top of Fluent UI, we will encounter users who have used Fluent UI in the past, and those who haven’t.
We should ensure that our components:
- Utilize & Extend as much of the Fluent UI styles and controls as possible.
- Reasonably expose the properties of underlying Fluent components.
- Use component properties/interfaces that look like Fluent UI component properties/interfaces, exposing similar functionality.
A responsive design is crucial to the modern web. Although responsiveness is subjective and will vary from customer to customer, our components should provide a default responsive look and feel on at least 3 standard devices namely Phone, Tablet and Desktop.
Responsiveness can be tested using Edge Dev Tools - Device Toolbar.
Ctrl + Shift + M
@media screen and (min-width: 1024px) {...}
iPad
can be used in Edge Device Toolbar for testing the UI on a tablet.
@media screen and (min-width: 768px) and (max-width: 1024px) {...}
Galaxy S5
can be used in Edge Device Toolbar for testing the UI on a phone.
@media screen and (max-width: 640px) {...}
A UI component should expose properties that allow a developer to observe events such as buttonClicked
.
To implement a customizable UI component we recommend creating a component on top of FluentUI's base components and expose the ability for a user to inject custom CSS styling.
The UI Library has adapted a custom styling mechanism similar to what Fluent UI uses.
Each component should expose a styles
property that allows users to provide custom CSS rules to the root
component and children
components (when applicable).
Following code sample shows it's implementation.
interface VideoTileStylesProps {
/** Styles for the root container */
root?: IStyle;
/** Styles for video container */
videoContainer?: IStyle;
/** Styles for container overlaid on the video container */
overlayContainer?: IStyle;
}
interface VideoTileProps {
/** Custom styles */
styles?: VideoTileStylesProps;
/** ...add more props as needed */
}
function VideoTile (props: VideoTileProps): JSX.Element {
return (
<Stack className={mergeStyles(rootStyles, styles?.root)}>
<Stack className={mergeStyles(videoContainerStyles, styles?.videoContainer)}>
{renderElement}
</Stack>
<Stack className={mergeStyles(overlayContainerStyles, styles?.overlayContainer)}>
{children}
</Stack>
</Stack>
);
};
Note:
-
A component should always expose a
styles
property with at least theroot
property that allows a user to style the component root/wrapper (outer appearance of the component). -
mergeStyles
is part of the@fluentui/merge-styles
library. Through the use ofmergeStyles
, developers can provide default styles to the components. These styles get merged with customstyles
provided by a user. The order in which styles are passed to themergeStyles
function is important. Styles should be passed tomergeStyles
in the order of priority, where the first parameter has the lowest and the last has the hights priority. Documentation formergeStyles
can be found here: https://github.com/microsoft/fluentui/blob/master/packages/merge-styles/README.md -
Each property inside the
styles
property must be of the typeIStyle
defined in Fluent UI. Documentation forIStyle
can be found here: https://developer.microsoft.com/fluentui#/controls/web/references/istyle
Fluent UI has a rich component library and can be leveraged to build new custom components.
The following code sample shows how a custom button can be built on top of a Fluent UI DefaultButton
export const CustomButton = (props: ControlButtonProps): JSX.Element => {
// style set contains style for root, flexContainer, textContainer, label, etc...
const componentStyles = concatStyleSets(controlButtonStyles, props.styles ?? {});
return (
<DefaultButton
disabled={props.disabled}
onClick={props.onClick}
showLabel={props.showLabel}
styles={componentStyles}
menuIconProps={props.menuIconProps}
menuProps={props.menuProps}
onRenderIcon={() => <Icon iconName={props.icon.iconName} className={iconStyle} />}
/>
);
};
Refer to Fluent UI documentation for more components here: https://developer.microsoft.com/fluentui#/controls/web