border-image
CSS property και SVG
Με λίγα λόγια, το border-image
είναι ένα CSS property που μας επιτρέπει να διακοσμήσουμε ένα κουτί “πάνω από” (ή στον χώρο του) border
. Το MDN έχει πλήρη τεκμηρίωση για αυτά τα (όχι πολύ δημοφιλή) properties και values, αλλά εγώ θα χρησιμοποιήσω SVG αντί για bitmaps ή gradients, και θα σημειώσω μερικά ενδιαφέροντα σημεία.
Ήταν πριν από 7 χρόνια που έγραψα για το border-image
για πρώτη φορά (υπάρχει ένα σχεδόν πλήρες snapshot εκείνου του άρθρου στο Internet Archive, στα Ελληνικά), για να «στυλάρω» ένα button χωρίς background-image
η την τεχνική sliding doors. Εκείνη την εποχή, η υποστήριξη για border-image
δεν ήταν πολύ καλή, και ταυτόχρονα οι διάφορες versions του Microsoft Internet Explorer είχαν σχετικά μεγάλο μερίδιο της «αγοράς» και δεν υποστήριζαν border-image
καθόλου. Ήταν μπελάς, έπρεπε να χρησιμοποιηθεί JavaScript για να βγει το ίδιο «εφέ» σε διαφορετικούς browser ενώ το fallback ήταν τουλάχιστον άσχημο. Όμως, αυτές τις μέρες οι περισσότεροι browsers υποστηρίζουν border-image
ενώ έχουμε στη διάθεσή μας και το @supports
feature query, για την περίπτωση που πρέπει να υποστηρίξουμε legacy user agents.
Ας αρχίσουμε με μία κομμένη και μικρότερη σε διαστάσεις εικόνα, που μετέτραψα σε SVG βιαστικά και εν συνεχεία βελτιστοποίησα για να γλυτώσω bytes. Σύμφωνα με τα specs, το border-image
property είναι shorthand για τα border-image-source
, border-image-slice
, border-image-width
, border-image-outset
και border-image-repeat
, με αυτή την σειρά. Μόνο το border-image-source
είναι απαραίτητο, αλλά η προεπιλογή του border-image-slice
είναι 100% (της εικόνας) οπότε το τελικό εφέ πιθανότατα δεν θα αυτό που περιμένετε. Η ιδέα είναι το «κόψιμο» της εικόνας σε εννέα περιοχές — τέσσερις γωνίες, τέσσερις άκρες και το κέντρο το οποίο προς το παρόν θα αγνοήσω. Οι γωνίες μεταχειρίζονται διαφορετικά από τις άκρες, και αυτή είναι η ιδιαιτερότητα του border-image
γενικώς! Βρήκα την επεξηγηματική εικόνα αυτού του κοψίματος στο MDN κάπως παραπλανητική, με το “left” λεκτικό τοποθετημένω πάνω από την πρώτη (γωνιακή) περιοχή και το “bottom” τοποθετημένο στην τέταρτη γωνιακή περιοχή, οπότε καλό είναι να θυμάστε πως η σειρά των πιθανών τιμών του border-image-slice
είναι top, right, bottom, left, όπως άλλωστε συμβαίνει με τα margin
s, padding
s, border
s και άλλα CSS properties.
Το SVG για το border-image-source
προέκυψε από το PNG και μετά του άλλαξα διαστάσεις σε ένα τυχαίο 180 pixels πλάτος και ύψος, μόνο και μόνο για να είναι μικρότερο από τα κουτιά με τα παραδείγματα που ακολουθούν· άδεια <span>
s με 440 px max-width
και 200 px ύψος:
border-style: solid;
border-width: 50px;
border-image: url("border-image-css-and-svg.svg");
Box content
Τα specs ορίζουν πως τα border-style
και border-width
πρέπει να έχουν τιμές για να «δουλέψει» το border-image-source
. Αν δεν δώσουμε συγκεκριμένες τιμές στο border-image-slice
, η εικόνα θα φανεί σε όλες τις γωνίες σύμφωνα με το μέγεθος του border-width
— ούτε ιδιαίτερα χρήσιμο ούτε όμορφο! Κατά πάσα πιθανότητα δεν θα αφήνετε το border-image
με όλα τα defaults, οπότε ας προχωρήσουμε.
border-style: solid;
border-width: 50px;
border-image-source: url("border-image-css-and-svg.svg");
border-image-slice: 50;
Box content
Κάτι κάνουμε τώρα! Χρησιμοποίησα ένα “μαγικό” αριθμό, 50 pixels για border-width
και border-image-slice
. Με αυτό τον τρόπο, το border είναι πιο παχύ απ’ όσο χρειάζεται. 37 pixels είναι μάλλον καταλληλότερο για αυτή την εικόνα· το κόψιμο θα γίνει πάνω στα «μπιλάκια» κοντά στις γωνίες. Υπενθυμίζω πως το SVG προέκυψε με πρόχειρο trace από το PNG και ίσως να μην είναι «καθαρό»:
border-style: solid;
border-width: 37px;
border-image-source: url("border-image-css-and-svg.svg");
border-image-slice: 37;
Box content
Το αποτέλεσμα είναι σχεδόν ίδιο με πριν αλλά το περιεχόμενο είναι μεγαλύτερο. Το μισό κάθε μπίλιας τεντώνεται οριζόντια (και κάθετα, αλλά το ύψος δεν είναι αρκετά μεγάλο για να φανεί) αφού το slicing κόβει τις μπίλιες στην μέση. Για να δούμε όμως και το PNG (border-image-css-and-svg.png):
border-style: solid;
border-width: 37px;
border-image-source: url("border-image-css-and-svg.png");
border-image-slice: 37;
Box content
Δεν είναι πολύ άσχημο, αλλά το SVG απλώνει καλύτερα στις πάνω και κάτω άκρες, όπως ήταν αναμενώμενο. Θα συνεχίσω με το SVG και θα δοκιμάσω κάποια ακόμα properties, όπως το border-image-width
:
border-style: solid;
border-width: 37px;
border-image-source: url("border-image-css-and-svg.svg");
border-image-slice: 37;
border-image-width: 1.5;
Box content
Το αποτέλεσμα του border-image-width
δεν είναι εύκολα κατανοητό, αφού βασίζεται στην μονάδα μέτρησης των τιμών. Μία τιμή χωρίς μονάδα μέτρησης (unitless) θα τεντώσει/σμικρύνει την εικόνα σε σχέση με το border-width
ενώ μία τιμή επί της εκατό θα κάνει το ίδιο σε σχέση με το πλάτος ή το ύψος της εκάστοτε περιοχής!. Για να δούμε ένα ποσοστό:
border-style: solid;
border-width: 37px;
border-image-source: url("border-image-css-and-svg.svg");
border-image-slice: 37;
border-image-width: 150%;
Box content
Το αποτέλεσμα είναι αρκετά ενδιαφέρον αλλά χάθηκαν οι άκρες! Παρατηρήστε πως το border-image
«ρεντάρεται» κάτω από το background του περιεχομένου. Για να δούμε τι θα γίνει αν μικρύνουμε τις τιμές των border-width
και border-image-slice
:
border-style: solid;
border-width: 24px;
border-image-source: url("border-image-css-and-svg.svg");
border-image-slice: 24;
border-image-width: 150%;
Box content
Εντάξει, εδώ χάνεται λίγο η μπάλα! Αφαίρεσα και το background του content ώστε να φανεί όλο το αποτέλεσμα, το οποίο είναι μάλλον… απρόβλεπτο, και ίσως γι’ αυτό το border-image
να μην χρησιμοποιείται πολύ συχνά, οπότε καλύτερα να παραμείνουμε σε «λογικές» τιμές στο εξής! Να δούμε τώρα τι κάνει το border-image-outset
;
border-style: solid;
border-width: 37px;
border-image-source: url("border-image-css-and-svg.svg");
border-image-slice: 37;
border-image-outset: 18px;
Box content
Το border-image-outset
δέχεται ένα θετικό αριθμό και καθορίζει την απόσταση της εξωτερικής άκρης του border-image
από την εξωτερική άκρη του border
. Οι διαστάσεις του κουτιού δεν αλλάζουν, και το border-image
δεν προκαλεί overflow ή/και scrollbars — όπως άλλωστε και το box-shadow
. Για αυτό τον λόγο «χύνεται» πάνω σε οτιδήποτε υπάρχει γύρω από το κουτί. Και τέλος, τι κάνει το border-image-repeat
;
border-style: solid;
border-width: 37px;
border-image-source: url("border-image-css-and-svg.svg");
border-image-slice: 37;
border-image-repeat: round stretch;
Box content
Το border-image-repeat
είναι η «μαγεία» που δύσκολα επιτυγχάνεται με άλλα CSS properties. Δυστυχώς, δέχεται μόνο μία ή δύο τιμές. Αν δώσουμε μόνο ένα keyword από τα stretch
, repeat
, round
, space
(η προεπιλεγμένη τιμή είναι stretch
) τότε αυτό θα αφορά σε όλες τις άκρες, αλλιώς το πρώτο keyword ρυθμίζει τις πάνω και κάτω περιοχές, ενώ το δεύτερο ρυθμίζει την εμφάνιση των δεξιά και αριστερά. Στο συγκεκριμένο παράδειγμα, ο browser χρησιμοποιεί πολλές φορές τα πάνω και κάτω κομμάτια της εικόνας ενώ τεντώνει ή σμικρύνει το πλάτος ώστε να γεμίσουν οι περιοχές και να κουμπώσουν τα γραφικά στις γωνίες. Για τις άκρες δεξιά και αριστερά το αντίστοιχο κομμάτι της εικόνας τεντώνεται για να πιάσει όλο το ύψος.
Το ίδιο περίπου αποτέλεσμα μπορεί να επιτευχθεί με background-image
και background-size: 100% 100%
, αλλά το εφέ επανάληψης-και-προσαρμογής-μεγέθους στις άκρες δεν μπορεί να γίνει κατ’ αυτόν τον τρόπο. Μπορούν ωστόσο να χρησιμοποιηθούν πολλάπλα background-image
s αλλά δύσκολα θα ταιριάξουν τα γραφικά στη σύνδεση αν τα κουτιά είναι ελαστικά! Παραδέχομαι πως το border-image
δεν είναι κάτι που θα χρησιμοποιήσετε συχνά, αλλά είναι χρήσιμο εργαλείο, καλό είναι να το έχετε υπόψιν!