iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
😺

CSS Strategies and Component Classification in Component-Based Development: My Personal Approach

に公開1件
  • I feel like the typical CSS know-how specific to component-based development in React or Svelte doesn't really apply.
  • I posted this on Twitter and it got a good response, so I'll summarize it here in my own way.
  • Markup engineers sometimes tell me that the management unit of a React Component and the management policy of layout as CSS are different, but I ignore such talk and make them match completely. That's just the nature of how I develop.

Component Classification

  • Logic Components
  • Layout Components
  • Block Components
  • Inline Components

Definitions

  • Logic Components
    • Handle only data processing such as Providers or hooks, and pass data to children.
    • They do not have any CSS or DOM presence.
  • Layout Components
    • A layout component has multiple child block components (or slots), and its sole responsibility is to decide the placement of those child block components.
    • Layout components other than the root are basically width: 100%, height: 100%; margin: 0; padding: 0; box-sizing: border-box; with display: grid or display: flex. Grid is recommended.
    • A layout component divides its own area using flex, grid, etc.
    • The number of block components that can be placed in each divided area is 1 or 0.
    • Responsive implementation is also handled here.
  • Block Components
    • A block component is an element that fills the area provided by the layout.
    • The root element of a block component is width: 100%; height: 100%; margin: 0; padding: 0; box-sizing: border-box; display: flex;.
    • The interior of a block component consists of either a single layout component or multiple inline components.
  • Inline Components
    • Elements that do not have a fixed height or width, where size is determined by their own content.
    • Block components or layout components wrapped in a fixed-size container element with specific width and height are treated as inline components.
  • Exceptional Treatment
    • In my experience, when the data and DOM structure are tightly coupled, such as with DnD (Drag and Drop), it is impossible or difficult to separate logic components from everything else.
    • If the service has an application-like layout, the root element of the layout component is often width: 100vw; height: 100vh;. In traditional vertically-expanding services, parts of the layout near the root use height: auto; overflow: auto;.

As a result, it becomes a repetition of Layout and Block, such as Root => Layout => Block => Inline or Layout => Block => ...

Sample

  • Styles are written inline for explanation purposes. (Since assuming a specific CSS framework would require its own explanation...)
  • I wrote this casually for explanatory purposes, so while I prioritized code readability, I haven't actually checked what gets rendered.
// Logic Component
const DataContext = createContext<{ value: number }>({ value: 0 });

function RootProvider(props: {children: React.ReactElement}) {
  return <DataContext.Provider value={{ value: 1 }}>
    {props.children}
  </DataContext.Provider>;
}

// Layout Component
function RootLayout(props: {
  header: React.ReactElement;
  content: React.ReactElement;
  footer: React.ReactElement;
}) {
  return <div style={{
    width: "100vw",
    height: "100vh",
    overflow: "hidden",
    display: "grid",
    gridTemplateAreas: `
      'header'
      'content'
      'footer'
    `,
    gridTemplateColumns: "1fr",
    gridTemplateRows: "100px 1fr 300px"
  }}>
    <div style={{gridArea: "header"}}>{props.header}</div>
    <div style={{gridArea: "content"}}>{props.content}</div>
    <div style={{gridArea: "content"}}>{props.footer}</div>
  </div>
}

// Inline Component
function Title() {
  return <div>Hello, Example</div>;
}

// Block Component
function HeaderBlock() {
  return <div style={{
    width: "100%",
    height: "100%",
    margin: "0",
    padding: "0",
    display: "flex",
  }}>
    Header
  </div>;
}

// Block Component
function ContentBlock() {
  return <div style={{
    width: "100%",
    height: "100%",
    margin: "0",
    padding: "0",
    display: "flex",
  }}>
    <Title />
    Content~~~
  </div>;
}

// Block Component
function FooterBlock() {
  return <div style={{
    width: "100%",
    height: "100%",
    margin: "0",
    padding: "0",
    display: "flex",
  }}>
    Footer
  </div>;
}

// Root Component
function App() {
  return <RootProvider>
    <RootLayout
      header={<HeaderBlock />}
      content={<ContentBlock />}
      footer={<FooterBlock />}
    />
  </RootProvider>
}

Benefits

  • The type of component is naturally categorized based on which CSS properties are written.
  • Block components are always treated as width: 100%, height: 100%, so any block is interchangeable.
  • By separating layout and block, the design is resilient to changes in element placement, and implementing responsive design or multiple display modes is easy.
  • Since the height is predetermined for most elements, it is friendly for skeleton screen implementation and critical rendering path optimization.

Drawbacks

  • It is difficult to maintain a consistent height inheritance policy for liquid layouts.
  • Block components can easily overflow if you're not careful.
  • It can hardly be called a conventional (web production) style.

Discussion

neji_bitneji_bit

記事、読みました!
記事の冒頭にある「コンポーネントの種類リスト」が興味深かったです。
この4つは、mizchiさん流の分類方法なのでしょうか。「自分ではこういう名称で分けている」がまとまっている方は、一緒に仕事をする際にありがたいですね。
CSSデザインについては、私もWebアプリ等を趣味レベルで作るのですが「とにかくまずはgridレイアウト」で色々なんとかなる、と感じていますw 具体的にはCSSファイルの先頭に * {display: grid;} 書いてるくらいw