Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global styles #139

Closed
niksajanjic opened this issue Sep 14, 2016 · 14 comments
Closed

Global styles #139

niksajanjic opened this issue Sep 14, 2016 · 14 comments

Comments

@niksajanjic
Copy link

Maybe I'm missing something but currently I'm having a problem with Aphrodite to write global styles like normalize and my own default styles. For instance, sometimes I want to reset all my anchors to not have underline by default and I always reset all my tags to be border boxed:

a {
  text-decoraton: none;
}
* {
  box-sizing: border-box;
}

As far as I see, all I can do is to insert style sheet on my own that will have these resets but I would rather have a component similar to Radium.js Style component that does this for me adding autoprefix automatically.

Any info about this specific problem?

@kentcdodds
Copy link
Contributor

We should probably add something like this in the README, but aphrodite is concerned with styling components. Global styles are not a thing that you do with aphrodite. For me, I just use regular CSS for that stuff (and I don't have much of it).

Some people would suggest that you could create an aphrodite-styled component for the <a> and use that instead of the raw <a>. I'm not entirely convinced on this, but that would be another approach.

@niksajanjic
Copy link
Author

niksajanjic commented Sep 14, 2016

Makes sense to me to go either way you guys decide, but if you stay with the approach of only styling components just please add that to README so people would know that they have to manually add normalize and other resets and worry about prefixes and dynamically assigning values (this probably means using one another module besides Aphrodite).

The reason why this is needed is although your suggestion about <a> component sounds doable, that isn't possible for my second example with * {} style, plus it's much easier to just add default styles that apply to all tags around app than creating component for each tag you want to reset to default behavior. These are my defaults that I was adding using Radium's Style component:

body: {
  backgroundColor: colors.background,
  boxSizing: 'border-box',
  color: colors.font,
  fontFamily: fonts.main,
  fontSize: fonts.size,
  lineHeight: 1,
  '-moz-osx-font-smoothing': 'grayscale',
  '-webkit-font-smoothing': 'antialiased',
},
'*': {
  boxSizing: 'inherit',
  lineHeight: 'inherit',
},
'p, h1, h2, h3, h4, h5, h6, ul': {
  margin: 0,
  padding: 0,
},
h1: {
  fontSize: '2.5em',
  fontWeight: 'normal',
},
h2: {
  fontSize: '1.8em',
},
h3: {
  fontSize: '1.5em',
},
h4: {
  fontSize: '1.2em',
},
a: {
  color: 'inherit',
  textDecoration: 'none',
},
li: {
  listStyle: 'none',
},
'input, select': {
  border: `1px solid ${color(colors.font).alpha(0.1).rgbaString()}`,
  borderRadius: sizes.radius,
  outline: 'none',
  padding: '12px 15px',
}

Doing this with Aphrodite on a component level really isn't an option as it adds too much work. Also, this adds prefixes for me so I don't have to worry about and it's possible to assign variables to each rule which I can keep in another file and share across app.

EDIT: If you don't intend to support global styles this issue can be closed, just please add a note to README about this specific issue. Thank you.

@kentcdodds
Copy link
Contributor

Yeah, I'd just put that in a CSS file and import it via webpack or just inline it in the index.html. You normally don't need to change those kinds of styles all that often, so I don't mind doing vendor prefixes by hand.

If you don't intend to support global styles this issue can be closed, just please add a note to README about this specific issue. Thank you.

Would you like to makeapullrequest.com?

@niksajanjic
Copy link
Author

Thanks for the offer but I would rather leave that to you guys just to mention it somewhere in FAQ or whichever place you desire. I will still go about finding a way to translate this JS object (which is much bigger for real apps, this was just a simple profile editor) into stylesheet so I can use autoprefixer and variables which I can't do with CSS and if I use SASS or LESS than I have to keep 2 different files with variables, variables.scss and varibales.js which is a bad solution. Thanks for the effort.

@kentcdodds
Copy link
Contributor

If it were me with a sizeable file like you suggest, I'd just use webpack + postcss

@jlfwong
Copy link
Collaborator

jlfwong commented Sep 16, 2016

Thanks for filing the issue! This and a number of other issues do suggest it's unclear what Aphrodite's stance on this is (which is exactly what @kentcdodds said), so an inclusion in the README would probably be helpful.

Will leave open until a README change lands.

@jschr
Copy link

jschr commented Sep 21, 2016

I've been using aphrodite for a while, one thing I've struggled with is how to style the html, body or #root elements. Being able to style the html element is particularly important if you're using rems.

I usually ended up with a small global stylesheet so I made react-style-side-effect to help keep all styling in aphrodite (doesn't address the overall global styles issue). Posting in case this helps anyone else!

@joshwcomeau
Copy link
Contributor

I created a PR to include documentation about this, as it's something I spent a few minutes trying to figure out. I'm not 100% satisfied with my solutions though.

The issue is of communication; in a personal project, I've created a "style-variables" file that holds colours, sizes, etc... This works great; I can import it into all my components, and use those variables in the Aphrodite stylesheets.

When I want to add these same colours/sizes into the global styles, though, I have to copy/paste their values into the static CSS.

I think it may be worthwhile to create an addon/extension that allows us to inject global styles into the <style> object created by Aphrodite? I agree that it's outside the concern of the base Aphrodite project, but I think a compatible solution should be created.

@jschr's solution is good for targeting specific document elements, but I want something more generic. I want to be able to do:

'input, select, textarea': {
  padding: '0',
  borderBottom: '3px solid #F00',
}

@niksajanjic
Copy link
Author

Just to let people know how we handled this issue on our side. We are using Aphrodite to style React components and we are satisfied at the moment. There are few minor issues with placeholders not being autoprefixed and styling child elements when parent is hovered (or any other state), but besides that we were able to solve most issues that we had with inline styles in past.

We are using Normalize.css to make browser resets. We are using Webpack with css-loader and style-loader which handles our normalize.css file and adds it to our head tag. Same goes for our Font Awesome, but that is out of the scope of this problem.

We also keep variables.js and defaults.js files. Variables keep all our colors, sizes, transitions, fonts and any other reusable styling constant for our application. We use defaults.js file to write additional default stylings for our specific application, like styling body tag, adding default font sizes, families, default styling for all links, buttons, ul, ol, etc. Now, to be able to insert these rules as global stylings like we did with normalize we are using PostCss, basically postcss-loader and postcss-js. This takes care of our defaults.js file which gets inserted into head tag.

We are writting defaults.js in JS on purpose cos we are reusing variables at the same time for our global styles and our aphrodite rules. There's an issue with PostCss and babel@6 that we had to fix by exporting both files with module.exports instead of using ES6. Also, it looks a bit as overkill to use css-loader, style-loader, postcss-loader, postcss-js, json-loader and autoprefixer just to put some global stylings in head tag, but until we find better way we are keeping it like this for now.

@jaydenseric
Copy link

I just tried to migrate from Radium and not being able to set global styles is an immediate challenge. I'm not using dodgy global resets or anything, simply trying to get rid of the margin on body, etc. Because the apps must mount inside body there is not elegant way to reach out to apply (at a minimum) these styles:

html: {
  textSizeAdjust: '100%'
},
body: {
  margin: 0,
  backgroundColor: 'white'
}

For now I have inlined the body styles in my HTML template, but it is a shame that the vendor prefixes of textSizeAdjust are manual and can not share the same tooling/config as the rest of the project.

@joshwcomeau
Copy link
Contributor

joshwcomeau commented Nov 30, 2016

@jaydenseric so, since this issue was opened, an official solution now exists :) it's in the form of writing an extension to handle it.

The example extension covers exactly this use-case. Effectively the API winds up looking like:

const styles = StyleSheet.create({
    globals: {
        '*html': {
            textSizeAdjust: '100%'
        },
        '*body': {
            margin: 0,
            backgroundColor: 'white'
        }
    }
});

@thisbejim
Copy link

To be clear for anyone who is trying to do the above:

The extend method returns a whole new aphrodite object.

const globalSelectorHandler = (selector, _, generateSubtreeStyles) => {
  if (selector[0] !== '*') {
    return null;
  }
  return generateSubtreeStyles(selector.slice(1));
};
const globalExtension = { selectorHandler: globalSelectorHandler };
const extended = StyleSheet.extend([globalExtension]);

Then create your styles:

const styles = extended.StyleSheet.create({
  globals: {
    '*html': {
      textSizeAdjust: '100%'
    },
    '*body': {
      margin: 0,
      backgroundColor: 'white'
    }
  }
});

Then apply your styles:

extended.css(styles.globals);

@niksajanjic
Copy link
Author

I suppose this can now be closed. I also tried and was successful in making global styles available, but docs were also a bit confusing to me at first, so here's how I did it using docs and @thisbejim example:

defaults.js

import { StyleSheet } from 'aphrodite/no-important';
import { colors, fonts } from './variables';

const GLOBALS = '__GLOBAL_STYLES__';

const globalExtension = {
  selectorHandler: (selector, baseSelector, generateSubtreeStyles) =>
    (baseSelector.includes(GLOBALS) ? generateSubtreeStyles(selector) : null),
};

const extended = StyleSheet.extend([globalExtension]);

const styles = extended.StyleSheet.create({
  [GLOBALS]: {
    body: {
      ...
    },
    '*': {
      ...
    },
    'p, h1, h2, h3, h4, h5, h6, ul, dl, dt, dd': {
      ...
    },
    'button, input, optgroup, select, textarea': {
      ...
    },
    'a, button': {
      ...
    },
    a: {
      ...
    },
  },
});

export default extended.css(styles[GLOBALS]);

And then I'm just importing it into my index.js file:

import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import { browserHistory } from 'react-router';
import store from './store';
import Routes from './routes';
import './styles/defaults'; // global styles through Aphrodite

ReactDOM.render(
  <Routes store={store} history={browserHistory} />,
  document.getElementById('root'),
);

I don't want to put * (asterisk) before all tag names so I check for __GLOBAL_STYLES__ to be included into baseSelector passed down. Just make sure you don't use __GLOBAL_STYLES__ anywhere else in your Aphrodite stylesheet creation process and don't have 2 files that create global styles because you could have unexpected behavior.

@xymostech
Copy link
Contributor

@niksajanjic Glad you figured out the extension API. If you look at the PR where I introduced the StyleSheet.extend thing, I wrote up a function for adding global styles without you having to add the *s: #95 (comment) which might make your job easier.

Maybe I should commit it and put it on npm...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants