Scaffolds and in-line CSS styles

Hi Redwood team! :wave:

Is there any thought (or an item on the roadmap) to centralize CSS styles that are needed for the scaffold generator? Right now styles are applied mostly in-line or in a CSS const at the top of a component. For each model it looks like 7 files get created that have inline styling. This makes it difficult to change the look and feel of a RedwoodJS app without doing quite a bit of editing.

I wish I had a suggestion, but there are quite a few tools available here: CSS Modules, PostCSS, SASS, to name a few.

We’re open to suggestions! Scaffolds were my baby, and I’m a huge fan of TailwindCSS, but didn’t want to include another dependency like PostCSS just for scaffolds, which some folks may never even use. I followed Rails’s lead and just created a static CSS file that only appeared in your filesystem if you created a scaffold.

But I agree that once the scaffolds are in place they’re not very easy to re-style if you decide to do so. Utility CSS is an amazing paradigm for creating pages with zero new CSS, but doesn’t lend itself to CSS Zen Garden-like updates to your styles…

2 Likes

I’m new to TailwindCSS, but they seem to have some advice here:

There are two recommended approaches:

Create a template partial or JavaScript component

Here’s an example of a of how you could re-factor the code for creating a new user and pull out a component called NewTemplate that encapsulates those styles:

import { useMutation } from '@redwoodjs/web'
import { navigate, routes } from '@redwoodjs/router'
import UserForm from 'src/components/UserForm'

const CREATE_POST_MUTATION = gql`
  mutation CreateUserMutation($input: UserInput!) {
    createUser(input: $input) {
      id
    }
  }
`

const NewUser = () => {
  const [createUser, { loading, error }] = useMutation(CREATE_POST_MUTATION, {
    onCompleted: () => {
      navigate(routes.users())
    },
  })

  const onSave = (input) => {
    createUser({ variables: { input } })
  }

  // this component would live in its own file
  const NewTemplate = (props) => {
    return (
      <div className="bg-white border rounded-lg overflow-hidden">
        <header className="bg-gray-300 text-gray-700 py-3 px-4">
          <h2 className="text-sm font-semibold">New {props.model}</h2>
        </header>
        <div className="bg-gray-100 p-4">{props.children}</div>
      </div>
    )
  }

  return (
    <NewTemplate model="User">
      <UserForm onSave={onSave} loading={loading} error={error} />
    </NewTemplate>
  )
}
export default NewUser

Extracting CSS components with @apply

I know less about how this would work, but it sounds like you would have to add Tailwind as a dependency to RedwooJS, since there’s probably some build-time magic.

I hope this helps!

Yeah I love Tailwind’s components, but you need to include the full PostCSS stack for those @apply directives to work, and we really don’t want to do that just for the scaffolds.

Likewise we’re not too keen on complicating the scaffolds with additional templates just for the sake of styling.

My first instinct is just to go with classic semantic names for classes so that you have one thing to change if you want to restyle, prefixed with rw- so they (hopefully) don’t clash with anything you create yourself. Something like

<div className="rw-wrapper">
  <header className="rw-header">
    <h2 className="rw-title">New {props.model}</h2>
  </header>
  <div className="rw-body">{props.children}</div>
</div>

That example you gave (semantic names) was my first instinct too. That’s generally how I’ve built web apps as long as I can remember. But it’s also explicitly contrary to how the Tailwind team thinks about CSS and the idea of “utility first”:

I don’t have a strong preference, but I think if you’re going to standardize on Tailwind, users would be better served if the Tailwind usage adhered to their best practices.

I wouldn’t say we’ve standardized on Tailwind, the scaffolds are the only thing that comes pre-styled. I just happen to be madly in love with Tailwind and so I use it for everything by default. And as much as it pains me to go back to semantic-style class names that’s probably the way to go if people want to easily re-style.

Although now that you can get CSS-in-JS and JS-in-CSS and styled components and a million other paradigms it probably doesn’t matter what we do, it won’t work for 90% of people. :neutral_face:

1 Like

Another bit of feedback while we’re talking about scaffolds. For every model that an app has, I’m counting 4 pages directories and 7 component directories being created. For an app with even a moderate number of models (4), that’s 16 and 28 directories being created under src/pages and src/components respectively.

Would you consider adding a directory level based on the model?

Something like: src/components/{model}/ and src/pages/{model} ?

1 Like

We’ve got another thread discussing this same thing, although I can’t find it now…I can’t remember if it was here in the forum or an actual GitHub issue…

2 Likes

Hi gents, Was this the one?

That looks right to me. I gave the issue a :+1:

One thing that I might look into building for myself (and will happily share) is a set of React components that can dynamically deliver a scaffold CRUD experience based on a model name. This won’t be useful for anyone that wants to modify the code generated by the scaffold CLI, but it will be useful for folks that want a low-touch “PHP MySQL Admin”-style interface.

No, there was another thread somewhere where me and the person were going back and forth on possible syntaxes, like do we add a --prefix option, or can you specify a path with the name of the model, like yarn rw g scaffold admin/post…

Maybe this, then?

2 Likes

There’s a PR for creating scaffolds in a subdirectory: https://github.com/redwoodjs/redwood/pull/423