Skip to content

Commit

Permalink
Merge pull request #32 from HyunmoAhn/docs/line-spacing
Browse files Browse the repository at this point in the history
Docs/line spacing
  • Loading branch information
HyunmoAhn authored Nov 1, 2024
2 parents 084a167 + 5035422 commit 331a842
Show file tree
Hide file tree
Showing 14 changed files with 2,235 additions and 119 deletions.
Binary file modified .yarn/install-state.gz
Binary file not shown.
6 changes: 5 additions & 1 deletion docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,10 @@ module.exports = {
authorsMapPath: '../authors.yml',
},
theme: {
customCss: require.resolve('./src/css/custom.css'),
customCss: [
require.resolve('@radix-ui/themes/styles.css'),
require.resolve('./src/css/custom.css'),
],
},
googleAnalytics: {
trackingID: 'UA-206743648-1',
Expand All @@ -153,6 +156,7 @@ module.exports = {
],
],
plugins: [
'docusaurus-plugin-sass',
[
'@docusaurus/plugin-content-blog',
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
slug: line-spacing-on-css
title: How to implement line-spacing in CSS
description: Explains how to implement line spacing in CSS
keywords:
- css
- web
authors: HyunmoAhn
tags: [css, line-spacing, web, issue, trouble-shooting, line-height]
---

## Introduction

In CSS, line-height is used to adjust line spacing. However, in a project I worked on,
there was a requirement to support line-spacing. This article explains how to implement line-spacing using CSS.

import { LinePaddingExample, LinePlayground } from '@site/src/code-snippet/lineSpacing/index.tsx'

<LinePaddingExample />

## Cheat Sheet

Unlike line-height, which applies padding both above and below the text, line-spacing applies padding only between lines.
The difference here is that line-spacing does not add padding at the beginning and end of the text block.

You can implement line-spacing using margins.

```tsx
const lineHeight = 20; // origin line-height
const lineSpacing = 10;
const margin = - (lineSpacing / 2);

const LineSpacingText = () => {
return (
<p style={{
// highlight-start
marginTop: `${margin}px`,
marginBottom: `${margin}px`,
lineHeight: `${lineHeight + lineSpacing}px`,
// highlight-end
}}>
Hello, World!
</p>
);
};
```


## Playground
<LinePlayground />

<!--truncate-->

## Additional Story

There isn't much more to say about the topic of line-spacing.
If you have the idea of "Can it be implemented in CSS?", it's a difficulty level that can be sufficiently implemented.

The key to this idea is that while `line-height` applies padding both above and below, `line-spacing` applies padding only between lines.
Therefore, even in the case of `line-height`, the spacing between lines is the same as the `line-spacing` padding,
and the difference is the presence or absence of padding on the first and last lines.
To bridge this difference, you use margins to reduce the value by `line-spacing / 2`.

In general web development, if such a `line-spacing` requirement comes up,
it is better to somehow persuade in another direction or propose an alternative.
In my case, although it was on the web, I had to create a design tool that perfectly matched a screen developed native,
and I had to satisfy the special specification of `line-spacing`, so I used this method.
It is not recommended to use this method simply to meet design requirements.
Nevertheless, I hope this article helps developers who need to meet similar requirements.

import { Playground } from '@site/src/components/playground/index.tsx'
import { codeSnippet } from '@site/src/code-snippet/lineSpacing/index.tsx'


## Code Playground

<Playground title={'line-spacing'} files={codeSnippet} height={900} outputHeight={400} />
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,21 @@
"@docusaurus/preset-classic": "3.5.2",
"@docusaurus/theme-mermaid": "^3.5.2",
"@mdx-js/react": "^3.1.0",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/themes": "^3.1.4",
"@svgr/webpack": "^8.0.1",
"classnames": "^2.5.1",
"clsx": "^2.0.0",
"docusaurus-plugin-sass": "^0.2.5",
"file-loader": "^6.2.0",
"lucide-react": "^0.274.0",
"prism-react-renderer": "^2.4.0",
"raw-loader": "^4.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"sass": "^1.80.5",
"styled-components": "^6.0.7",
"url-loader": "^4.1.1"
},
Expand Down
137 changes: 137 additions & 0 deletions src/code-snippet/lineSpacing/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import cx from 'classnames';
import { useState, useId } from 'react';
import { Select } from '@radix-ui/themes';
import * as Label from '@radix-ui/react-label';
import style from './lineSpacing.module.scss';
import lineSpacingSnippet from '!!raw-loader!./lineSpacingSnippet';

const TEXT_SAMPLE =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet.';

export const LineSpacingGuide = () => {
return (
<div className={style.guideWrapper}>
<h4>Line Spacing</h4>
<div className={cx(style.lineSpacingGuideContainer, style.guideContainer)}>
<p>Line Gap</p>
<p className={style.lineSpacingGuide} style={{ backgroundColor: 'red' }} />
<p>Line Gap</p>
<p className={style.lineSpacingGuide} style={{ backgroundColor: 'green' }} />
<p>Line Gap</p>
</div>
<p className={style.caption}>Line Spacing (5px)</p>
</div>
);
};

export const LineHeightGuide = () => {
return (
<div className={style.guideWrapper}>
<h4>Line Height</h4>
<div className={cx(style.lineHeightGuideContainer, style.guideContainer)}>
<p className={style.lineHeightGuide} style={{ backgroundColor: 'red' }} />
<p>Line Gap</p>
<p className={style.lineHeightGuide} style={{ backgroundColor: 'red' }} />
<p className={style.lineHeightGuide} style={{ backgroundColor: 'green' }} />
<p>Line Gap</p>
<p className={style.lineHeightGuide} style={{ backgroundColor: 'green' }} />
<p className={style.lineHeightGuide} style={{ backgroundColor: 'blue' }} />
<p>Line Gap</p>
<p className={style.lineHeightGuide} style={{ backgroundColor: 'blue' }} />
</div>
<p className={style.caption}>Line Height (5px)</p>
</div>
);
};

export const LinePaddingExample = () => {
return (
<div className={style.container}>
<LineSpacingGuide />
<LineHeightGuide />
</div>
);
};

export const LineSpacingPlayground = ({ lineSpacing }: { lineSpacing: number }) => {
const paddingGap = lineSpacing / 2;

return (
<div>
<h3>Line Spacing {lineSpacing}px</h3>
<div className={style.playgroundBorder}>
<p
className={style.playground}
style={{
lineHeight: `${lineSpacing + 26}px`,
marginTop: `-${paddingGap}px`,
marginBottom: `-${paddingGap}px`,
}}
>
{TEXT_SAMPLE}
</p>
</div>
<p className={style.caption}>Line Spacing ({lineSpacing}px)</p>
</div>
);
};

export const LineHeightPlayground = ({ lineHeight }: { lineHeight: number }) => {
return (
<div>
<h3>Line Height ({lineHeight}px + fontSize)</h3>
<div className={style.playgroundBorder}>
<p className={style.playground} style={{ lineHeight: `${lineHeight + 26}px` }}>
{TEXT_SAMPLE}
</p>
</div>
<p className={style.caption}>Line Height ({lineHeight}px + fontSize)</p>
</div>
);
};

export const SpacingSelect = ({
value,
onChange,
}: {
value: number;
onChange: (value: string) => void;
}) => {
const id = useId();

return (
<div className={style.select}>
<Label.Root htmlFor={id}>Select Spacing</Label.Root>
<Select.Root value={String(value)} onValueChange={onChange}>
<Select.Trigger id={id} />
<Select.Content>
<Select.Item value="0">0px</Select.Item>
<Select.Item value="5">5px</Select.Item>
<Select.Item value="10">10px</Select.Item>
<Select.Item value="15">15px</Select.Item>
<Select.Item value="25">25px</Select.Item>
<Select.Item value="30">30px</Select.Item>
<Select.Item value="40">40px</Select.Item>
</Select.Content>
</Select.Root>
</div>
);
};

export const LinePlayground = () => {
const [value, setState] = useState(0);

return (
<div className={style.linePlaygroundWrapper}>
<SpacingSelect value={value} onChange={(selected) => setState(Number(selected))} />
<div className={style.linePlayground}>
<LineSpacingPlayground lineSpacing={value} />
<LineHeightPlayground lineHeight={value} />
</div>
</div>
);
};

export const codeSnippet = {
'/App.js': lineSpacingSnippet,
};
92 changes: 92 additions & 0 deletions src/code-snippet/lineSpacing/lineSpacing.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
.container {
display: flex;
justify-content: space-around;
column-gap: 20px;
}

.guideWrapper {
display: flex;
flex-direction: column;
}


.guideContainer {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
line-height: 1;
min-width: 150px;
font-size: 26px;
font-family: serif;
overflow: hidden;
text-align: center ;

border: 1px solid var(--ifm-color-primary-dark);
border-radius: 5px;
padding: 3px;

p {
margin: 0;
width: 100%;
}
}

.caption {
margin-left: 3px;
margin-top: 3px;
font-size: 14px;
font-weight: 500;
color: var(--ifm-color-primary-dark);
}

.lineSpacingGuide {
height: 10px;
}

.lineHeightGuide {
height: 5px;
}

.playgroundBorder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 26px;
border: 1px solid var(--ifm-color-primary-dark);
border-radius: 5px;
padding: 3px;
margin-bottom: 3px;
}

.playground {
font-size: 26px;
margin-bottom: 0;

transition: all 0.3s ease;
}

.linePlayground {
display: flex;
flex-direction: row;
justify-content: space-between;
column-gap: 20px;
}

.linePlaygroundWrapper {
display: flex;
flex-direction: column;
row-gap: 10px;
}



.select {
display: flex;
justify-content: flex-start;
align-items: center;
column-gap: 10px;
background-color: var(--dark-background-color);
margin-bottom: 10px;
}
37 changes: 37 additions & 0 deletions src/code-snippet/lineSpacing/lineSpacingSnippet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useState } from 'react';

export default function App() {
const [lineSpacing, setLineSpacing] = useState(0);

return (
<>
<h3>
Line Spacing:{' '}
<input type="text" value={lineSpacing} onChange={(e) => setLineSpacing(e.target.value)} />
px
</h3>
<LineSpacingPlayground lineSpacing={Number(lineSpacing)} />
</>
);
}

export const LineSpacingPlayground = ({ lineSpacing }) => {
const paddingGap = lineSpacing / 2;

return (
<div>
<p
style={{
width: '200px',
fontSize: '26px',
lineHeight: `${lineSpacing + 26}px`,
marginTop: `-${paddingGap}px`,
marginBottom: `-${paddingGap}px`,
}}
>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.
Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet.
</p>
</div>
);
};
Loading

0 comments on commit 331a842

Please sign in to comment.