# CSS Text Outline: How to Make Creative Typography Stroke

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><a target="_self" rel="noopener noreferrer nofollow" href="https://purecode.ai/blogs/css-outline-text" style="pointer-events: none">Originally posted here</a>.</div>
</div>

In the intricate world of web design today, typography serves as a powerful means of expression, improving site usability and it goes beyond the realms of font choice and sizes. In this article we’ll explore a sub-section of Web Typography, that is, dealing with CSS text outline, also known as typography stroke, along with the various methods we can follow to achieve stroke effect in CSS.

Web typography, often referred to as web text, constitutes a form of vector-based graphics within the digital landscape. This characteristic enables web typography to scale seamlessly, mirroring the behaviour of standard Scalable Vector Graphics (SVGs). The scalability of web typography is a key feature that ensures optimal visual experiences across a diverse range of devices and screen sizes.

In essence, the vector-based nature of web typography means that the characters are defined by mathematical equations rather than fixed pixels. This inherent flexibility allows text elements to adapt gracefully, maintaining clarity and legibility whether viewed on a small smartphone screen or a large desktop monitor.

Since we’ve established that Web typography is essentially vector-based graphics, then Web typography should also be able to do things just like design software that deals with vector-based graphics would, e.g. Adobe Illustrator.

## **What is Text Outline or Typography Stroke?**

In the context of design, a “**stroke**” typically refers to the outline of a shape or text element. It is a visual attribute that adds a defined boundary to a shape or text, helping it stand out and providing emphasis. Meanwhile, a “**fill**“ refers to the interior area of a shape or object that is enclosed by its outline or border. It is the part of the element that is solid or filled with colour, pattern, or texture.

In the realm of typography, a “text stroke” or “typography stroke” or “text outline” specifically refers to the outlined border around the characters of a text.

Here’s an image that illustrate where the stroke and fill are located:

![](https://blogs.purecode.ai/blogs/wp-content/uploads/2023/12/320ddb9b-005d-4bc3-9dc0-8c56dbe434c5.png align="left")

With CSS (Cascading Style Sheets), **strokes** can be applied to text using properties such as **text-stroke**, enhancing the visual appeal of typography on the web. These strokes can be customised to achieve different effects, contributing to the overall aesthetic and readability of the text. Whether it’s a subtle accent or a bold statement, understanding and utilising strokes play a crucial role in graphic design and digital communication.

## **Stroked Typography in CSS**

In CSS, stroked typography can be created in three methods:

* Using the non-standard [webkit-text-stroke](https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-text-stroke) property
    
* Using text-shadow property
    
* Using pseudo-element hack
    

### **Using the non-standard webkit-text-stroke property**

The [**webkit-text-stroke**](https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-text-stroke) property is a non-standard CSS property that is used to apply **width** and **color** of strokes or outlines to text characters. It’s a shorthand property for the longhand properties [\-webkit-text-stroke-width](https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-text-stroke-width) and [\-webkit-text-stroke-color](https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-text-stroke-color).

The example below is equivalent:

```markdown
-webkit-text-stroke: 4px navy;

OR

-webkit-text-stroke-width: 4px;
-webkit-text-stroke-color: navy;
```

Here’s an example of how you can use **\-webkit-text-stroke** property:

```markdown
/* CSS file */

h1 {
  /* Apply a black stroke with a width of 2 pixels */
  -webkit-text-stroke: 2px black;

  /* Set the text fill color to white */
  color: white;

  /* Optional: Adjust other styling properties */
  font-size: 180px;
  font-family: 'Arial', sans-serif;
  text-align: center;
}
```

```markdown
<!-- HTML file -->

<h1>Hello World</h1>
```

### **Preview**

![Hello World text with stroke applied](https://blogs.purecode.ai/blogs/wp-content/uploads/2023/12/d092d84b-9067-4e10-9112-07779d72423f.png align="left")

In this example, the **\-webkit-text-stroke** property is applied to an h1 element, creating a black stroke around the text. The color property is set to white to ensure that the text inside the stroke is filled with a contrasting color.

From the example above, everything seems to work fine, but let’s say we increase the **\-webkit-text-stroke-width** property value to 20px and change the color to red to see the effect properly.

```markdown
/* CSS file */

h1 {
  -webkit-text-stroke-width: 20px;
  -webkit-text-stroke-color: black;

  color: red;

  font-size: 180px;
  font-family: 'Arial', sans-serif;
  text-align: center;
}
```

### **Preview**

![-webkit-text-stroke-width increased](https://blogs.purecode.ai/blogs/wp-content/uploads/2023/12/7863203b-5558-4787-a9cd-2bbfcea6767d.png align="left")

Wow, now the text starts looking really ugly and misshapen. This is because the stroke is positioned above the fill and resides within the confines of the text. In simpler terms, the stroke is above the fill or color of the text, placed in the center of the text. So how do we fix this?

The sad reality is that there’s no proper fix for this issue yet, as of 2023, and even design software like Adobe Illustrator has this same problem, where with texts, you can’t change the alignment of the stroke from being centered.

Here’s a GIF to illustrate the same issue with Adobe Illustrator 2022.

![](https://blogs.purecode.ai/blogs/wp-content/uploads/2023/12/9dead888-4179-4cb7-ac70-db6434ccb332.png align="left")

However, there’s a property that kinda fixes this issue, but as of 2023, it only works on Firefox browser. The CSS property is called [paint-order](https://developer.mozilla.org/en-US/docs/Web/CSS/paint-order), which helps to control the order in which the fill and stroke text content are drawn. With the **paint-order** CSS property, the stroke will be placed outside the text color or fill.

### **Here’s how it looks on Firefox browser (Windows)**

![](https://blogs.purecode.ai/blogs/wp-content/uploads/2023/12/33c825dd-4dff-4a0f-8f30-cd907a0736e2.png align="left")

Here’s what changed in the CSS file, and all we introduced was **paint-order: stroke** which does the magic.

```markdown
/* CSS file */

h1 {
  -webkit-text-stroke: 20px black;
  color: red;

  /* Set Paint-Order property */
  paint-order: stroke;

  font-size: 180px;
  font-family: 'Aria', sans-serif;
  text-align: center;
}
```

> Notice on Firefox browser, the stroke corners are curved by default.

On Google Chrome browser, the result remains the same, the **paint-order** property has no effect at all, sadly 😑.

> Here’s the [CodePen link](https://codepen.io/jome-favourite/pen/MWLPYGo) if you’d like to preview it for yourself.

The **\-webkit-text-stoke, -webkit-text-stroke-width,** and **\-webkit-text-stroke-color** CSS properties all have good support in major browsers, and according to [caniuse.com](https://caniuse.com/) it has [96.89% support across all major browsers](https://caniuse.com/?search=-webkit-text-stroke) as of November 2023.

#### **Pros**

1. Easy to use, as it requires a single property
    
2. [Very well supported in modern browsers](https://caniuse.com/?search=-webkit-text-stroke)
    

#### **Cons**

1. Achieving a satisfactory result can be challenging when combining a colour and a stroke.Though the **paint-order** property helps, but it’s only supported in one browser.
    
2. Being a non-standard property, there’s a possibility that it might be removed and substituted in future updates. Additionally, there could be variations or inconsistencies across different browsers.
    

### **Using text-shadow property**

The **text-shadow** method is more of a hacky solution, where multiple shadows with different offsets can be used to simulate the text stoke. With the approach, we are guaranteed that the simulated stroke with shadows would also be beneath the text content at every point.

If you think about it, we’ll need the shadows to cover every direction of each character in the text, i.e. top, right, bottom, and left. Let’s see an example:

```markdown
/* CSS File */

h1 {
  /* Simulate text-stroke with multiple shadows */
  text-shadow: 
     1px 1px 0px #000,   /* Bottom right */
     -1px -1px 0px #000,  /* Top left */
     1px -1px 0px #000,  /* Top right */
     -1px 1px 0px #000;   /* Bottom left */     
        
  /* Optional: Additional styling */
  color: #fff;  /* Text color */
  font-size: 180px;
  font-family: 'Arial', sans-serif;
  text-align: center;
}
```

**Preview**

![Hello World with text shadow hack](https://blogs.purecode.ai/blogs/wp-content/uploads/2023/12/42d566cf-b807-47ea-a33f-8fd5c8b7f91f.png align="left")

> See the preview on [CodePen](https://codepen.io/jome-favourite/pen/eYxPNzj)

In this example, the text-shadow property is used to create a simulated text-stroke effect by applying shadows in each direction. Additionally, the shadows have a 0px blur radius (0px), ensuring a crisp outline.

Also, looking at the preview, it looks pretty close to what we had first with the **\-webkit-text-stroke** property, but there’s a challenge, how do we tell what the [offset X and Y length](https://developer.mozilla.org/en-US/docs/Web/CSS/text-shadow) values should be? Let’s say we increased the offset X and Y length values to 3px. So, let’s see what happens:

```markdown
/* CSS File */

h1 {
  /* Simulate text-stroke with multiple shadows */
  text-shadow: 
     3px 3px 0px #000,    /* Bottom right */
     -3px -3px 0px #000,  /* Top left */
     3px -3px 0px #000,   /* Top right */
     -3px 3px 0px #000;   /* Bottom left */     
        
  /* Optional: Additional styling */
  color: #fff;  /* Text color */
  font-size: 180px;
  font-family: 'Arial', sans-serif;
  text-align: center;
}
```

![text shadow - text stroke effect with 3px offset x and y length values](https://blogs.purecode.ai/blogs/wp-content/uploads/2023/12/83fd493e-f6fb-427e-be88-7c7639dec9e0.png align="left")

> Here’s the [CodePen link](https://codepen.io/jome-favourite/pen/ExrdjmM) to preview

With keen eyes, you’d notice that there are gaps in some characters of the text, like the **“e”** and **“r”**. To help show the gap, here is the same image but zoomed in.

![](https://blogs.purecode.ai/blogs/wp-content/uploads/2023/12/b3568f73-e741-4d2b-a40d-68c7da6186c0.png align="left")

![](https://blogs.purecode.ai/blogs/wp-content/uploads/2023/12/db1661ff-1ff2-456a-9452-08ed3cfa7ccf.png align="left")

To fix the gaps, we’ll need to add more shadows, but then this method isn’t sufficient cause you’d likely be guessing the shadow offset would be tedious, except you’re a maths wizard 😅. Here’s an article that shows how using [8 multiple shadows solved](https://unused-css.com/blog/css-text-strokeoutline/#using-8-box-shadows) the gap problem.

#### **Pros**

1. It works properly when the offset X and Y length values are just 1px.
    
2. It is widely supported across all major browsers
    

#### **Cons**

1. Introduces gaps in the simulated stroke effect if the shadow X and Y offset is greater than 1px
    
2. It’s too hacky cause you’d have to guess the shadow offsets.
    
3. Strokes aren’t shape as it should, they may appear blurry.
    

### **Using pseudo-element hack**

With the help of pseudo-elements, we can simulate the text stroke effect by creating a copy of the text content and then apply the **\-webket-text-stroke** property to the pseudo-element. With the [**attr()**](https://developer.mozilla.org/en-US/docs/Web/CSS/attr) CSS function and [**data-\* attribute**](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes), we can achieve the text stroke effect.

Enough explanation; let’s see an example to understand how to utilise the pseudo-element hack.

```markdown
<h1 data-text="Hello World">Hello World</h1>
```

The **data-\*** attribute is used on the HTML element in order for the attribute value “Hello World” to be gotten from CSS with the help of the **attr()**. Here’s how we get the **data-text** attribute value in CSS.

```markdown
h1 {
   color: red;
   font-size: 180px;
   font-family: 'Aria', sans-serif;
   position: relative;
}

h1::after {
   /* data-text atrribute value gotten as the content value in CSS */
   content: attr(data-text);

   position: absolute;
   top: 0;
   left: 0;

   /* Place the pseudo-element below the actual text */
   z-index: -1;
   /* Relative to h1 */
   font-size: 1em;

   -webkit-text-stroke: 20px black;
}
```

**Preview**

![Hello World text stroke effect with Pseudo element ](https://blogs.purecode.ai/blogs/wp-content/uploads/2023/12/b137f23f-116a-454e-b1e6-b1f10fa1f44f.png align="left")

> Here’s the [CodePen link](https://codepen.io/jome-favourite/pen/mdvzWyQ)

In the example above, we had a lot of properties flying around, like the position **relative** and **absolute** values. On the **h1** CSS type selector, notice the **position: relative** CSS declarion, it’s very important, cause the **h1** pseudo element selector would be positioned absolute to the h1 element. Also, the **top**, and **left** property has the value 0.

The issue with the pseudo-element hack, is that if the h1 selector changes its position, let’s say the text is aligned to the center, the text stroke effect breaks. Here’s an example to see how it breaks:

```markdown
h1 {
   color: red;
   font-size: 180px;
   font-family: 'Aria', sans-serif;
   position: relative;

   /* New CSS declaration added */
   text-align: center;
}

h1::after {
   /* data-text atrribute value gotten as the content value in CSS */
   content: attr(data-text);

   position: absolute;
   top: 0;
   left: 0;

   /* Place the pseudo-element below the actual text */
   z-index: -1;
   /* Relative to h1 */
   font-size: 1em;

   -webkit-text-stroke: 20px black;
}
```

**Preview**

![](https://blogs.purecode.ai/blogs/wp-content/uploads/2023/12/3cd368e6-9f43-4783-a603-e7570a8614b2.png align="left")

#### **Pros:**

* Effectively generates a stroke around the text.
    
* The stroke adjusts proportionally when the font-size of the element (e.g., an h1 tag) changes.
    

#### **Cons:**

* Requires duplicating text content in a custom attribute.
    
* Shares similar drawbacks with **\-webkit-text-stroke-width** when used for stroke creation, potentially requiring mathematical adjustments to avoid using this property.
    
* Issues may arise when the element alignment is changed.
    

## **Combining text-stroke and text-shadow Properties**

Combining the **text-stroke** and **text-shadow** properties in CSS opens up a realm of possibilities for creating visually compelling and dynamic text effects. These properties, when used in conjunction, allow for intricate and layered styling, enhancing the overall aesthetic impact of your typography.

Here are some examples showcasing how you can combine **text-stroke** and **text-shadow**.

### **Example 1**

```markdown
h4 {
  /* Apply a text-stroke with a white color and a width of 2 pixels */
  -webkit-text-stroke: 2px white;
  color: black; /* Set the text color */

  /* Add a subtle text-shadow for depth and dimension */
  text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);

  /* Optional: Additional styling */
  font-size: 180px;
  font-family: 'Arial', sans-serif;
  text-align: center;
}
```

In this example, the **-webkit-text-stroke** property is used for the **text-stroke** effect, creating a white outline around the text characters. The text-shadow property is then applied to add a subtle shadow, providing depth and dimension to the text.

![text shadow and text stroke effect - example one](https://blogs.purecode.ai/blogs/wp-content/uploads/2023/12/dc1e7c2d-009b-4002-a567-33f08f4eb750.png align="left")

%[https://codepen.io/jome-favourite/pen/PoVypgq] 

### **Example 2**

To peek at the code used to achieve this example, here’s the [CodePen](https://codepen.io/jome-favourite/pen/poGxPgd) link:

![text shadow and text stroke effect - example two](https://blogs.purecode.ai/blogs/wp-content/uploads/2023/12/bca4f0f0-43d5-4c12-be29-5294493f0081.png align="left")

### **Example 3**

Here’s the [CodePen link](https://codepen.io/jome-favourite/pen/MWLPmeV), for the Neon effect:

![text shadow and text stroke effect - example three](https://blogs.purecode.ai/blogs/wp-content/uploads/2023/12/bb250c0a-c988-421f-bdf7-c2fe88aaa617.png align="left")

## **Creative Typography Stroke Examples**

Let’s look at more examples that are creative:

### **Example 1**

![Stroke effect - Example 1](https://blogs.purecode.ai/blogs/wp-content/uploads/2023/12/f00503c4-1a48-4963-90b8-bf2118246900.png align="left")

```markdown
<h1>Stroke</h1>
```

```markdown
body {
  background: black;
}

h1 {
  background-image: linear-gradient(90deg, #ff00d2, #fed90f, #00a2ff);
  background-clip: text;
  -webkit-text-stroke-color: transparent;
  -webkit-text-stroke-width: calc(1em / 16) ;
  -webkit-background-clip: text;
  color: #000119;
  font-size: 22vmin;
  font-weight: 700;
  text-align: center;
}
```

> *This stroke effect is referenced from the* [*video by SoftCode*](https://www.youtube.com/watch?v=icnixsbLpQg)
> 
> [Codepen link to view the preview.](https://codepen.io/jome-favourite/pen/jOdemZz)

%[https://codepen.io/jome-favourite/pen/jOdemZz] 

### **Example 2**

![Lemon text stoke effect](https://blogs.purecode.ai/blogs/wp-content/uploads/2023/12/ec20a3ce-119f-4fb4-b3b9-39ecf9ef67eb.png align="left")

```markdown
<h1>Lemon</h1>
```

```markdown
@import url('https://fonts.cdnfonts.com/css/spicy-rice');

body {
  background-image: url(https://st3.depositphotos.com/5489530/13659/i/600/depositphotos_136593476-stock-photo-lime-and-mint-detox-water.jpg);
  background-color: #5DB318;
  background-size: cover;
  background-repeat: no-repeat;
  background-position: bottom bottom;
}

h1 {  
color: rgba(154, 41, 162, 0);
    font-family: "Spicy Rice", Sans-serif;
    font-size: 165px;
    font-weight: normal;
    text-shadow: -6px 10px 0px #5DB318;
    mix-blend-mode: darken;
    -webkit-text-stroke: 2px #FFFFFF;
    text-stroke: 2px #FFFFFF;
    text-align: right;
}
```

> *The stroke effect was reference in* [*happyaddons website*](https://happyaddons.com/text-stroke/)
> 
> [Codepen link to view the preview](https://codepen.io/jome-favourite/pen/eYxPWXX)

%[https://codepen.io/jome-favourite/pen/eYxPWXX] 

## **Browser Support For text-stroke Property**

According to [caniuse.com](https://caniuse.com/?search=text%20stroke), 96.89% of all browsers support the **\-webkit-text-stroke** property except for all versions of Internet Explorer browser and Opera mini browser. However, the other browsers support the text-stroke property by making use of the webkit prefix to ensure compatibility.

![Caniuse.com](https://blogs.purecode.ai/blogs/wp-content/uploads/2023/12/7e4bac6e-3973-4662-8b4b-09e29300c68a.png align="left")

## **Wrapping Up**

In wrapping up our look at text-stroke in CSS, we’ve covered a few cool ways to make your text stand out. Whether you’re using the **\-webkit-text-stroke** property for a basic outline, getting creative with **pseudo-elements**, or adding depth with **text-shadow**, these methods give you options to make your typography pop. In addition, each approach has its strengths, offering flexibility for different design needs. So, play around, try them out, and let your text be a canvas for your creativity!

Here are also some helpful resources about the text-stroke:

* [Adding Stroke to Web Text](https://css-tricks.com/adding-stroke-to-web-text/)
    
* [CSS Text Stroke/Outline](https://unused-css.com/blog/css-text-strokeoutline/)
    
* [3 Simple Techniques to Create a Text Stroke in HTML and CSS](https://www.youtube.com/watch?v=I4IENYMnyTw)
