Can I have different colored left and top borders in CSS?
Asked Answered
P

5

33

I would like to have a border that is 4px thick pink on the left and 1px grey elsewhere:

border: 1px solid #E5E5E5;
border-left: 4px solid #F24495;

The issue is the join is diagonal so I get a horrible overlay. I tried:

.item:before{ 
  border-left: 4px solid #F24495;
}

But no luck.

jsFiddle Example

Screenshot

Screenshot

Purington answered 15/6, 2012 at 14:5 Comment(3)
I don't understand what you mean by a straight joint, and how that differs from your screenshot.Skimpy
What you seem to be asking for (a vertically or horizontally aligned join between top border and left border) isn't possible with CSS alone as far as I'm aware.Clarkin
Perhaps use vector graphics (I don't know).Holliehollifield
J
37

.item::before was the right approach, but it needs a bit of work past a single border-left property. You'll need to make the pseudo element visible (display: block; content: "";), position the pseudo element on the left side of .item, and stretch it to line up with the top and bottom borders properly.

While this can be done manually, I highly recommend using CSS Variables (or variables in your preprocessor) since it makes updating the widths of borders less error-prone and painful.

.item {
  display: inline-block;
  padding: 0.2em 0.3em;
  background: #f8f8f8;
  color: #454545;

  /* Set border widths with variables */
  --top-border-width: 4px;
  --bottom-border-width: var(--top-border-width);
  --left-border-width: 16px;
  --right-border-width: var(--top-border-width);

  /* Set border styles for each side */
  border-top: var(--top-border-width) solid #e4e4e4;
  border-bottom: var(--bottom-border-width) solid #e4e4e4;
  border-right: var(--right-border-width) solid #e4e4e4;

  /* Remove the left border and add blank space where the border should be placed */
  border-left: 0;
  margin-left: var(--left-border-width);

  /* Contain the ::before */
  position: relative;
}

.item::before {
  /* Give the pseudo element substance */
  display: block;
  content: "";

  /* Add a left border with a straight edge */
  border-left: var(--left-border-width) solid #f84995;

  /* Position pseudo element's border where the normal border would have been placed */
  position: absolute;
  top: calc(0px - var(--top-border-width));
  bottom: calc(0px - var(--bottom-border-width));
  left: calc(0px - var(--left-border-width));
}
<h1 class="item">Gen.2</h1>
Jennijennica answered 15/6, 2012 at 14:15 Comment(1)
Modified so that the top and bottom "contain" the left border, and also demonstrates treatment of right border in similar situation: jsfiddle.net/ex6ZR/11Zhukov
P
9

If you wish to use the :before pseudo selector you need to set some content as well. See for example this jsfiddle with the following sample code:

<div>Container</div>

CSS:

div {
    border: 10px solid black;
    border-left-width: 0;
}
div::before {
    border: 10px solid orange;
    border-right-width: 0;
    content: '';
}

Displays as:

Screenshot from working code

Edit
Hmm, although this should strictly answer the question, while trying to adapt my solution into the question's fiddle I find this doesn't play very well with paddings. Open to suggestions/edits/other answers that can handle that bit :(...

Piling answered 15/6, 2012 at 14:14 Comment(4)
I think this is going to help with a bit of work. The issue I have is the :before then ignores the padding. example picPurington
In fact I can fix this by adding the padding to the :beforePurington
@KevinMann, keep in mind this code breaks once multiple lines (either with <br> or simply wrapping) are introduced (example). However, my answer does not (example).Jennijennica
In the end I added padding-right: 5px; to the :before and the same to the item and it worked perfectly see herePurington
D
7

this should work but requires extra markup:

.outer {
    border: 1px solid #E5E5E5;
    border-left: 0;
}

.inner {
    border-left: 4px solid #F24495;
}

and

<div class="outer">
    <div class="inner">
        ...
    </div>
</div>
Docker answered 15/6, 2012 at 14:7 Comment(3)
This looks a little bit edgy with roundes corners though, make sure to set radius to 0 for the affected cornersGiulietta
A problem with this approach is that the top border will still overlap with the left border. Have a look at this jsfiddleGiulietta
You can have look at my answer below to see how both issues can be fixed with this approach.Giulietta
T
3

Background

By default CSS uses miter joints (45° angles) for all borders joints. Hence, to achieve square joints (90° angles) for any border, you can either use (1) inner box-shadow, (2) pseudo-elements or (3) background-image and multiple linear-gradients.

Let's assume you have the following element that you want to style:

<div></div>

Option 1: square joints using box-shadow

div {
  /* downside of using box-shadow, you need to add the   */
  /* inner border size to the padding on top of any      */
  /* additional padding you might want                   */
  padding: 20px;
  /* by changing the order of your box-shadows, you      */
  /* can modify which borders overlap each other         */
  box-shadow:
    /* left "inner border" */
    inset 20px 0 0 0 red,
    /* right "inner border" */
    inset -20px 0 0 0 grey,
    /* top "inner border" */
    inset 0 20px 0 0 grey,
    /* bottom "inner border" */
    inset 0 -20px 0 0 grey;
}

Option 2: square joints pseudo-elements

div {
  border: 20px solid grey;
}

div::before {
  position: absolute;
  background-color: red;
  content: "";
  width: 20px;
  /* we need to add the height of the top and bottom    */
  /* border to the pseudo-elements' height as the       */
  /* inset border is not included in the height of the  */
  /* div even when "box-sizing: border-box" is set.     */
  height: calc(100% + 20px + 20px);
  top: -20px;
  left: -20px;
}

Option 3: square joints using background-image and multiple linear-gradients

div {
  /* downside of using multiple linear-gradients, you   */
  /* need to add the inner border size to the padding   */
  /* on top of any additional padding you might want    */
  padding: calc(20px + 10px);
  background-image: 
    /* left "inner border" */
    linear-gradient(to right, red 20px, transparent 20px),
    /* right "inner border" */
    linear-gradient(to left, grey 20px, transparent 20px),
    /* top "inner border" */
    linear-gradient(grey 20px, transparent 20px),
    /* bottom "inner border" */
    linear-gradient(to top, grey 20px, transparent 20px);
}

Trivandrum answered 27/1, 2019 at 16:10 Comment(0)
G
0

A combination of wrapping two elements and using the border-width-property worked quite well for me. Note that when you are using border-radius as well you will have to unset the affected sides. I included this in my example since border-radius is quite common to be used with borders.

JSFiddle

Click here

Code

<div class="outer">
    <div class="inner">
        Some text
    </div>
</div>
.outer {
  margin: 10px;
  border-left: solid lightblue;
  border-radius: 3px;
  border-width: 0 0 0 8px;
}

.inner {
  border: 7px solid grey;
  border-bottom-right-radius: 3px;
  border-top-right-radius: 3px;
  border-left: 0;
  padding: 16px;
}

Output

enter image description here

Giulietta answered 5/12, 2023 at 7:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.