Pup

v1.x

The BasicsStylesheets

To stay inline with the broader JavaScript and React communities, Pup has adopted styled-components as its preferred means for authoring CSS. Styled components allow us to manage CSS side-by-side with our React components. This makes maintenance of CSS far easier to contend with, removes the "leaky styles" problem where cascading styles clash accidentally, and improves our ability to server-side render the application.

Using Styled Components

What's nice about using styled-components is that it allows us to use plain CSS along with some helpful features like nesting styles, autoprefixing CSS3+ properties, and allowing us to change styles in response to props dynamically.

On the right, we have a simple example of styled-components in use within Pup. The basic idea is that we write our styles as an actual React component. Using the styled method from the styled-components package, we create a new variable which will represent our styled component and assign it to styled.<HTML Element>.

So, if we'd like styled-components to return a <div></div> with a particular set of styles, we'd call to styled.div. If we wanted a <button></button>? We'd use styled.button. Here's the basic syntax to get familiar with:

const StyledExampleComponent = styled.div`
  background: #2a75f3;
  // Other styles here.
`;

Now, StyledExampleComponent exists as a React component that we can use inside of our other React components, for example:

const ExampleComponent = () => (
  <StyledExampleComponent>
    <p>Just some example text here.</p>
  </StyledExampleComponent>
);

Now, we'll get a <div></div> with a blue background and an unstyled paragraph tag inside. If we wanted to style the <p></p> as well...

const StyledExampleComponent = styled.div`
  background: #2a75f3;
  
  p {
    color: #fff;
  }
`;

we just add the element to the set of styles being applied to its parent. So, here, because the <StyledExampleComponent> contains the <p></p>, we can add the style for the paragraph text to the styles for it.

Learn more about the basics in the styled-components docs.

Changing styles based on props

One of the neater features of styled-components is its ability to dynamically change styles based on props. Extending our example above, assume that our <ExampleComponent /> received a prop called yellowText as a true/false value. If yellowText is true, we'd want the <p></p> tag inside of <StyledExampleComponent /> to be yellow instead of white.

First, let's update our usage of the component...

const ExampleComponent = ({ yellowText }) => (
  <StyledExampleComponent yellowText={yellowText} >
    <p>Just some example text here.</p>
  </StyledExampleComponent>
);

Just like a normal React component, a styled component can be assigned props. Here, we pass down the yellowText prop from our <ExampleComponent /> to <StyledExampleComponent />

const StyledExampleComponent = styled.div`
  background: #4285F4;
  
  p {
    color: ${props => (props.yellowText ? '#ffcf50' : '#ffffff')};
  }
`;

Using a JavaScript template string (also known as string interpolation), we call a function inside of our styled component's definition which receives the props passed to that styled component. From there, we conditionally decide whether or not to show the text as yellow based on the truthiness of the yellowText prop.

Changing styles based on props.
Changing styles based on props.

Even cooler? If props.yellowText was changed dynamically behind the scenes (e.g., a user checking a box to show text in yellow), whenever the yellowText prop changed we'd see our UI update automatically (i.e., React would re-render, as would the style component).

Learn more about this in the styled-components docs.

/imports/ui/components/InputHint/InputHint.js

Using polished mixins

In addition to styled-components, Pup also includes another library written by the author of styled-components called polished. It includes a set of helpful mixins for generating CSS—if you've ever worked with Sass/SCSS (Pup's former CSS solution), these are equivalent to mixins there, but written in JavaScript.

On the right, using our example from above, say we wanted to change the opacity of our text if yellowText was false. In our styles, we'd just call to the opacify() mixin from polished to change the opacity of the white text to 50% if yellowText is false. This may not seem terribly impressive at first glance, but it's worth exploring all of the mixins available in the polished docs.

Example – Using polished with styled-components
import { opacify } from 'polished';

const StyledExampleComponent = styled.div`
  background: #4285F4;
  
  p {
    color: ${props => (props.yellowText ? '#ffcf50' : opacify(0.5, '#ffffff'))};
  }
`;

Sass/Scss Stylesheets (Pup 1.4.0 and below)

While the majority of the styles in Pup are provided by Bootstrap, it does include a limited set of custom styles. Some of these styles are tweaks to the styles in Bootstrap—just our preferences—and some are helpers that you can use to customize styles to fit your own product.

Usage with SSR

If you're still relying on Sass/Scss stylesheets in your app but have upgraded the rest of your codebase to take advantage of Pup's server-side rendering (SSR) feature, you will need to make sure to wrap your import './MyComponent.scss'; statements in a client-side check like this:

if (Meteor.isClient) import './MyComponent.scss';

This will ensure that any .scss files will be evaluated on the client only, avoiding any SSR rendering errors.

Stylesheets in Pup are loaded in one of two ways: globally in /imports/ui/stylesheets/app.scss and directly from within React components.

Global stylesheets

Pup includes five global stylesheets that apply to the entire application, all located in /imports/ui/stylesheets:

app.scss is solely responsible for importing the global stylesheets listed below into one common file that can be loaded onto the client. app.scss itself is loaded onto the client in /imports/startup/client/index.js.

bootstrap-overrides.scss contains styles that override some of the stock styles in Bootstrap. These overrides are a matter of preference. You can use this file to store your own bootstrap overrides, or delete it entirely if you'd rather work with the purest possible implementation of Bootstrap's styles.

colors.scss contains Sass variables for colors used throughout Pup. These include the colors used in Bootstrap, as well as Pup-specific colors like those to match OAuth provider brands and Clever Beagle's (Pup's port of call) own branding. Feel free to change, tweak, or remove these as necessary.

forms.scss contains helper styles for forms and form elements in Pup.

mixins.scss contains two Sass mixins: breakpoint() used for applying different CSS based on the browser's current width (e.g., mobile styles vs. desktop styles—tuned to Bootstrap's CSS breakpoints) and ie() for applying styles that are specific to Internet Explorer browsers.

If you have any global styles of your own (those that apply to the entire app), it's best to store them in the /imports/ui/stylesheets directory and import them within /imports/ui/stylesheets/app.scss.

Component stylesheets

In addition to the global stylesheets listed above, some components in Pup have their own stylesheets. To keep things clean and organized, component-specific stylesheets are stored directly alongside the components themselves and imported within the component file. This makes the CSS easier to understand and maintain.

For example, the <InputHint /> component has its own stylesheet InputHint.scss, both of which are located in /imports/ui/components/InputHint. For consistency sake, notice that component-specific stylesheets are given the same name as the component, but retain the .scss file extension. On the right, we can see how this stylesheet is imported into the component file near the top.

If you define a component that has any styles of its own (those that apply specifically to that component), it's best to store them alongside the component, for exmaple /imports/ui/components/MyComponent/MyComponent.scss and import them within the component /imports/ui/components/MyComponent/MyComponent.js using import './MyComponent.scss';.
Example – /imports/ui/components/InputHint/InputHint.scss
@import '../../stylesheets/colors';

.InputHint {
  display: block;
  margin-top: 8px;
  font-style: italic;
  color: $gray-light;
  font-size: 13px;
}
Example – /imports/ui/components/InputHint/InputHint.js
import React from 'react';
import PropTypes from 'prop-types';

import './InputHint.scss';

const InputHint = ({ children }) => (
  <div className="InputHint">
    {children}
  </div>
);

InputHint.propTypes = {
  children: PropTypes.node.isRequired,
};

export default InputHint;