Nie spoczywając na laurach zerkam też na konkurencyjne blogi, nawet żeby dostać sińca. Jednym z nich jest CSS-Tricks. I właśnie tam trafiłem ostatnio na wpis dotyczący ramek z gradientem dla elementów HTML. Ramki dodatkowo mają zaokrąglone narożniki, co wcale nie ułatwia zadania. Przedstawiono tam dwie metody, jedna stricte dotycząca ramek z nadanym border-radius. Druga opierała się o border-image, która to właściwość opiera się border-radius - jedna z drugą nie współgra.
Drugi wstęp.
Pierwsza metoda wykorzystywała dwa elementy. Główny z przezroczystą ramką i czarnym tłem, a pod nim pseudoelement wypełniający całą przestrzeń elementu głównego, włącznie z ramką. Tło pseudoelementu zawierało gradient, który był widoczny tylko w miejscu przezroczystej ramki głównego elementu. Niestety nie ma w tym przypadku możliwości utworzenia przezroczystego tła, przez które prześwitywałoby tło strony pod głównym elementem. Jesteśmy więc zmuszeni do używania jednolitego tła strony, a przynajmniej tła pod elementem, i również identycznego tła (maski) w głównym elemencie.
Challenge?
Pokaże Ci dziś metodę na otrzymanie identycznego efektu, ale z użyciem jednego elementu (bez pseudoelementu). Kluczem do jego osiągnięcia będą właściwości: background-origin oraz background-clip. A jeszcze większym kluczem będzie to, że możemy przecież używać kilku teł dla jednego elementu. Wyczuwasz już podstęp? Jeśli tak, to najpierw przypomnijmy sobie co robią wymienione właściwości.
Background-origin, to chyba jedna z mniej znanych i używanych właściwości. Mówi ona o tym gdzie jest domyślne położenie tła dla elementu. Mamy trzy właściwości (oprócz initial oraz inherit):
- padding-box - tło jest pozycjonowane względem obszaru paddingu (mieści się w tych granicach), to jest domyślna wartość,
- border-box - tło startuje w granicach ramki,
- content-box - tło zaczyna się w zawartości (tam gdzie zaczyna się padding).
Jeśli element nie będzie mieć padding, to padding-box=content-box.
Jeśli chodzi o background-clip, to ma identyczne wartości, ale te mówią gdzie kończy się tło (która granica obcina je). Przy jednolitym tle czy gradiencie nie mają one większego znaczenia, jeśli chodzi o ewentualne straty w widoczności. Jeśli jednak tło jest niejednolite, jakaś piktografika czy zdjęcie, to jeśli ustawimy background-origin na border-box, a background-clip na content-box, to tło będzie obcięte o fragmenty wystające na ramkę oraz padding.
HTML wstyd pokazać, bo to po prostu jeden DIV. CSS natomiast wygląda tak:
div {
width: 200px;
height: 200px;
border-radius: 20px;
border: 4px solid transparent;
background-image: linear-gradient(white, white),
linear-gradient(
135deg,
#f0b7a1 0%,
#8c3310 50%,
#752201 51%,
#bf6e4e 100%
);
background-origin: border-box;
background-clip: content-box, border-box;
}
Mamy tu więc element z zaokrąglonymi narożnikami i przezroczystą ramką. Ramka jest tylko po to, aby rozpechnąć element i wykorzystać dodatkową przestrzeń na podłożenie jednego z teł. A tła będa dwa. Jedno to gradient od białego do białego, dziwny, ale nie możemy użyć tu jednolitego koloru, więc trzeba emulować tą sytuację. Drugie tło to jakiś przykładowy gradient. Kolejność teł ma znaczenie - biały będzie na najwyższej warstwie. Obydwa tła pozycjonujemy od najbardziej zewnętrznych granic, czyli od ramki. Można tu też ustawić wartość: content-box, border-box, bo z białym nie musimy startować od ramki, ale nie ma to znaczenia, bo i tak je obetniemy. Obetniemy je za pomocą background-clip. Pierwsze tło do granic zawartości, a ponieważ nie ma paddingu, to do początku ramki, a drugie tło obcinamy z końcem ramki, czyli po prostu przykrywa całość.
The winner is:
Drugi czelendżyk?
Jest jeszcze oczywiście SVG. W tym przypadku jako ramkę wykorzystamy element RECT, który wsadzimy do środka naszego DIV.
<div>
<svg>
<rect></rect>
</svg>
<span>
to jest jakiś tekst
</span>
</div>
+
div {
width: 200px;
height: 200px;
position: relative;
padding:10px;
box-sizing:border-box;
}
div span {
position: relative;
}
svg {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
}
svg rect {
stroke-width: 4px;
stroke: #f00;
fill: none;
rx: 20px;
ry: 20px;
width: calc(100% - 4px);
height: calc(100% - 4px);
x: 2px;
y: 2px;
}
Sam kontener SVG ustawiamy absolutnie, dzięki czemu do DIV możemy dodać dowolną zawartość i nie będą sobie wadzić. Atrubuty dla elementu RECT (prostokąta), możemy ustalać bezpośrednio w CSS. rx oraz ry, to długości promieni zaokrągleń. Mamy tu możliwość odrębnego ustawienia promienia w osi X oraz Y. stroke-width, to grubość naszej ramki. Może Cię zdziwić dlaczego width i height są pomniejszone o 4px, a nie o 8px (2*4px) lub nie został użyty box-sizing:border-box? Druga właściwość nie działa na elementy SVG, natomiast odejmujemy tylko grubość stroke-width, które traktujemy jak tasiemkę owieniętą wokół naszego RECT. Stroke nie stanowi jak border w CSS odrębnych elementów dla każej z czterech krawędzi (nie ma odrębnej lewej i prawej, oraz górnej i dolnej ramki).
Efekt będzie taki:
Póki co ustaliliśmy ramkę jednolitego koloru. Aby ułatwić sobie zadanie skorzystamy z generatora gradientów. Umożliwia on generowanie gradientu również jako SVG. Tak więc kreujemy nasz gradient, a z otrzymanego kodu wycinamy tylko fragment jak poniżej:
<svg xmlns="http://www.w3.org/2000/svg" width="200px" height="200px" >
<defs>
/* ten fragment nas interesuje */
<linearGradient id="gradient" x1="0%" y1="100%" x2="100%" y2="0%" >
<stop offset="0%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
<stop offset="25%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
<stop offset="50%" style="stop-color:rgb(5,193,255);stop-opacity:1" />
<stop offset="75%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
<stop offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
</linearGradient>
/* */
</defs>
<rect x="0" y="0" width="100%" height="100%" fill="url(#gradient)" />
</svg>
Kopiujemy go do wnętrza naszego SVG:
<div>
<svg>
<linearGradient id="gradient" x1="0%" y1="100%" x2="100%" y2="0%" >
<stop offset="0%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
<stop offset="25%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
<stop offset="50%" style="stop-color:rgb(5,193,255);stop-opacity:1" />
<stop offset="75%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
<stop offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
</linearGradient>
<rect stroke="url(#gradient)"></rect>
</svg>
<span>
to jest jakiś tekst
</span>
</div>
ID dla gradientu możesz nadać inne. Ważne, aby wartość dla stroke odnosiła się do tego ID. Z CSS wyrzucamy stroke: #f00 (nie używamy już jednolitego koloru, ale ten z wygenerowanego gradientu). Tu już nie ma problemu z możliwością pokazania tła pod naszą ramką.
Best of the best:
W pokojowym nastroju zapraszam Cię na kolejny wpis.
Przydatne linki:
Artykuł z CSS-Tricks
Generator gradientów