CSS: Fettdruck, ohne die Größe des Containers zu ändern

118

Ich habe ein horizontales Navigationsmenü, das im Grunde nur ein Menü ist, <ul>bei dem die Elemente nebeneinander eingestellt sind. Ich definiere keine Breite, sondern verwende einfach Auffüllen, da ich möchte, dass die Breiten durch die Breite des Menüelements definiert werden. Ich habe das aktuell ausgewählte Element fett gedruckt.

Das Problem ist, dass das Wort beim Fettdruck etwas breiter wird, wodurch sich der Rest der Elemente leicht nach links oder rechts verschiebt. Gibt es eine clevere Möglichkeit, dies zu verhindern? Etwas in der Art, wie die Polsterung angewiesen wird, die zusätzliche Breite, die durch die Fettschrift verursacht wird, zu ignorieren? Mein erster Gedanke war, einfach ein paar Pixel vom Abstand des "aktiven" Elements zu subtrahieren, aber dieser Betrag variiert.

Wenn möglich, möchte ich vermeiden, für jeden Eintrag eine statische Breite festzulegen und dann im Gegensatz zu meiner aktuellen Auffülllösung zu zentrieren, um zukünftige Änderungen an den Einträgen zu vereinfachen.

Mala
quelle
Warum ist die Größenänderung ein Problem? Ist das Layout irgendwie durcheinander?
Chaulky
Ich denke, Fragen zur Webprogrammierung werden am besten bei Stack Overflow gestellt. Vielleicht wird ein Mod dies dort migrieren.
Ciaran
2
Chaulky: Weil es einige Dinge gibt, die ich in Bezug auf das Layout mehr hasse, als wenn Schaltflächen, auf die Sie klicken sollen, herumspringen sollen
Mala,
doejo.com/blog/… ist eine Lösung, die genau das tut, worüber John und Blowski über jscript gesprochen haben.
Thebulfrog

Antworten:

152

Ich hatte das gleiche Problem, bekam aber mit einem kleinen Kompromiss einen ähnlichen Effekt. Stattdessen verwendete ich Textschatten.

li:hover {text-shadow:0px 0px 1px black;}

Hier ist ein Arbeitsbeispiel:

body {
  font-family: segoe ui;
}

ul li {
  display: inline-block;
  border-left: 1px solid silver;
  padding: 5px
}

.textshadow :hover {
  text-shadow: 0px 0px 1px black;
}

.textshadow-alt :hover {
  text-shadow: 1px 0px 0px black;
}

.bold :hover {
  font-weight: bold;
}
<ul class="textshadow">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
  <li><code>text-shadow: 0px 0px 1px black;</code></li>
</ul>

<ul class="textshadow-alt">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
  <li><code>text-shadow: 1px 0px 0px black;</code></li>
</ul>

<ul class="bold">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
  <li><code>font-weight: bold;</code></li>
</ul>

jsfiddle Beispiel

Thorgeir
quelle
51
Ich liebe diese Lösung, obwohl ich empfehle, diese auf 1px 0px 0px zu stellen. Sieht etwas mutiger aus.
M. Herold
Ich brauchte eine reine CSS-Lösung und das ist die Antwort.
JCasso
Du bist ein echtes Genie! Einfachheit macht es großartig!
lufi
1
Nur das Hinzufügen li:hover {text-shadow: 1px 0 0 black;}führte zu Text mit zu geringem Abstand zwischen den Buchstaben. Um das noch einmal zu verbessern, setzen wir auchli {letter-spacing: 1px;}
Patrick Hammer
5
Ich habe verwendet -0.5px 0 #fff, 0.5px 0 #fff, weil wir die kleine Lücke zwischen dem Text selbst und dem Schatten sehen konnten. Und auch wenn der Text fett ist, werden beide Seiten gewichtet.
Atorscho
101

Die beste Arbeitslösung mit :: after

HTML

<li title="EXAMPLE TEXT">
  EXAMPLE TEXT
</li>

CSS

li::after {
  display: block;
  content: attr(title);
  font-weight: bold;
  height: 1px;
  color: transparent;
  overflow: hidden;
  visibility: hidden;
}

Es wird ein unsichtbares Pseudoelement mit einer Breite von fettem Text hinzugefügt, das nach titleAttributen sortiert ist .

Die text-shadowLösung sieht auf dem Mac unnatürlich aus und nutzt nicht die Schönheit, die das Rendern von Text auf dem Mac bietet. :)

http://jsfiddle.net/85LbG/

Bildnachweis: https://stackoverflow.com/a/20249560/5061744

workaholic_gangster911
quelle
11
Dies ist die zuverlässigste und sauberste Lösung. Sie reserviert einfach Platz für fett gedruckten Text im Element. In meinem Fall verursachte das zusätzliche Pseudoelement eine Höhenänderung. Das Hinzufügen von Negativ margin-topzum ::afterBlock löste das Problem.
Vaclav Novotny
2
Diese Lösung ist großartig! Ich habe JS verwendet, um ein Attribut "Dateninhalt" hinzuzufügen, das den Text des Elements enthält, damit ich das Ändern des HTML-Codes vermeiden kann.
Mistykristie
1
Genial. Dies sollte die beste Antwort sein. Beachten Sie, dass Sie bei einem anderen Element als li möglicherweise display: block verwenden müssen. auf dem: nach übergeordneten Element.
Blackbam
Tolle Lösung! Funktioniert gut mit :: vorher auch, in meinem Fall verhindert dies zusätzlichen Rand am unteren Rand.
Thomas Champion
Vielen Dank für diese Lösung. Ich habe ursprünglich Textschatten verwendet, aber dies funktioniert in Safari nicht, da es Dezimalwerte abrundet. Da ich 0.1px verwendete, wurde dies auf 0 gerundet. Ihre Lösung funktionierte einwandfrei. Ich habe nur das title-Attribut durch name ersetzt, da ich nicht wollte, dass die Tooltips
angezeigt
31

Die tragbarste und optisch ansprechendste Lösung wäre die Verwendung text-shadow. Dies überarbeitet und zeigt Beispiele von Thorgeirs Antwort unter Verwendung von Alexxalis und meinen eigenen Optimierungen:

  li:hover { text-shadow: -0.06ex 0 black, 0.06ex 0 black; }

Dadurch werden winzige "Schatten" in Schwarz (verwenden Sie blackgegebenenfalls den Farbnamen / -code Ihrer Schrift anstelle von ) auf beiden Seiten jedes Buchstabens verwendet, wobei Einheiten verwendet werden, die beim Rendern der Schrift richtig skaliert werden.

Warnung Warnung: px Werte unterstützen zwar Dezimalwerte, sehen jedoch nicht so gut aus, wenn sich die Schriftgröße ändert (z. B. skaliert der Benutzer die Ansicht mit Ctrl+ +). Verwenden Sie stattdessen relative Werte.

Diese Antwort verwendet Bruchteile von exEinheiten, da sie mit der Schriftart skaliert werden. Erwarten Sie in den
meisten Browser-Standardeinstellungen *1ex8pxund damit 0.025ex0.1px.

Überzeugen Sie sich selbst:

li             { color: #000; } /* set text color just in case */
.shadow0       { text-shadow: inherit; }
.shadow2       { text-shadow: -0.02ex 0 #000, 0.02ex 0 #000; }
.shadow4       { text-shadow: -0.04ex 0 #000, 0.04ex 0 #000; }
.shadow6       { text-shadow: -0.06ex 0 #000, 0.06ex 0 #000; }
.shadow8       { text-shadow: -0.08ex 0 #000, 0.08ex 0 #000; }
.bold          { font-weight: bold; }
.bolder        { font-weight: bolder; }
.after span        { display:inline-block; font-weight: bold; } /* workaholic… */
.after:hover span  { font-weight:normal; }
.after span::after { content: attr(title); font-weight: bold; display:block; 
                     height: 0; overflow: hidden; }
.ltrsp         { letter-spacing:0; font-weight:bold; } /* @cgTag */
li.ltrsp:hover { letter-spacing:0.0125ex; }
li:hover       { font-weight: normal!important; text-shadow: none!important; }
<li class="shadow0">MmmIii123 This line tests shadow0 (plain)</li>
<li class="shadow2">MmmIii123 This line tests shadow2 (0.02ex)</li>
<li class="shadow4">MmmIii123 This line tests shadow4 (0.04ex)</li>
<li class="shadow6">MmmIii123 This line tests shadow6 (0.06ex)</li>
<li class="shadow8">MmmIii123 This line tests shadow8 (0.08ex)</li>
<li class="after"><span title="MmmIii123 This line tests [title]"
                   >MmmIii123 This line tests [title]</span> (@workaholic…)</li>
<li class="ltrsp"  >MmmIii123 This line tests ltrsp (@cgTag)</li>
<li class="bold"   >MmmIii123 This line tests bold</li>
<li class="bolder" >MmmIii123 This line tests bolder</li>
<li class="shadow2 bold">MmmIii123 This line tests shadow2 (0.02ex) + bold</li>
<li class="shadow4 bold">MmmIii123 This line tests shadow4 (0.04ex) + bold</li>
<li class="shadow6 bold">MmmIii123 This line tests shadow6 (0.06ex) + bold</li>
<li class="shadow8 bold">MmmIii123 This line tests shadow8 (0.08ex) + bold</li>

Bewegen Sie den Mauszeiger über die gerenderten Linien, um zu sehen, wie sie sich vom Standardtext unterscheiden.

Ändern Sie die Zoomstufe Ihres Browsers ( Ctrl+ +und Ctrl+ -), um zu sehen, wie sie variieren.

Zum Vergleich habe ich hier zwei weitere Lösungen hinzugefügt: den Buchstabenabstandstrick von @ cgTag , der nicht so gut funktioniert, da die Schriftbreitenbereiche erraten werden müssen, und den Trick :: nach dem Zeichnen von @ workaholic_gangster911 , der unangenehmen zusätzlichen Platz lässt, damit sich der fette Text erweitern kann ohne benachbarte Textelemente zu stupsen (ich setze die Zuordnung nach dem fett gedruckten Text, damit Sie sehen können, wie er sich nicht bewegt).


In Zukunft werden wir mehr haben variable Schriftarten von Dingen fähig wie das Ändern Schriftgrad über font-variation-settings. Die Browserunterstützung nimmt zu (Chrome 63+, Firefox 62+), erfordert jedoch immer noch mehr als nur Standardschriftarten, und nur wenige vorhandene Schriftarten unterstützen dies.

Wenn Sie eine variable Schriftart einbetten, können Sie CSS wie folgt verwenden:

/* Grade: Increase the typeface's relative weight/density */
@supports (font-variation-settings: 'GRAD' 150) {
  li:hover { font-variation-settings: 'GRAD' 150; }
}
/* Failover for older browsers: tiny shadows at right & left of the text
 * (replace both instances of "black" with the font color) */
@supports not (font-variation-settings: 'GRAD' 150) {
  li:hover { text-shadow: -0.06ex 0 black, 0.06ex 0 black; }
}

Im Mozilla Variable Fonts Guide finden Sie eine Live-Demo mit einem Schieberegler, mit dem Sie mit verschiedenen Noten spielen können. Googles Einführung in variable Schriftarten im Web enthält ein animiertes GIF, das das Umschalten zwischen einer hohen und einer nicht bewerteten Note demonstriert:

animierte Amstelvar Alpha-Schriftdemo mit umschaltbarer Gradachse

Adam Katz
quelle
1
So einfach, so schön.
Randy Hall
18

Ich habe festgestellt, dass die meisten Schriftarten dieselbe Größe haben, wenn Sie den Buchstabenabstand um 1 Pixel anpassen.

a {
   letter-spacing: 1px;
}

a:hover {
   font-weight: bold;
   letter-spacing: 0px;
}

Dies ändert zwar die reguläre Schriftart, sodass jeder Buchstabe einen zusätzlichen Pixelabstand hat. Bei Menüs sind die Titel so kurz, dass sie kein Problem darstellen.

Reactgular
quelle
3
Dies ist die beste und am meisten unterschätzte Lösung, obwohl ich sie so geändert habe, dass sie mit 0 beginnt und in Fettdruck zu einem negativen Buchstabenabstand führt. Außerdem müssen Sie es für jede Schriftart und Schriftgröße anpassen. Ich habe auch @ Thorgeirs Geige aktualisiert, um diese (als 2. Lösung) jsfiddle.net/dJcPn/50/
robotik
Moderne Browser unterstützen jetzt die Genauigkeit von Subpixeln. So können Sie den Abstand mit 0.98pxoder kleineren Brüchen fein einstellen .
Reactgular
Dies sieht etwas seltsam aus, da der Abstandsalgorithmus nicht genau der gleiche ist (einige Buchstaben tanzen ein wenig herum), und was noch wichtiger ist, dies funktioniert nicht, wenn Schriftarten skaliert werden, selbst wenn Sie die 1pxin relative Werte wie 0.025exoder konvertieren 0.0125ex. Siehe meine Antwort für eine Live-Demonstration.
Adam Katz
@AdamKatz Ich vermute, dass sich das Rendern von Schriftarten geändert hat, seit diese Antwort veröffentlicht wurde, und es hängt stark davon ab, welche Schriftart Sie verwenden.
Reactgular
5

Für eine aktuellere Antwort können Sie Folgendes verwenden -webkit-text-stroke-width:

.element {
  font-weight: normal;
}

.element:hover {
  -webkit-text-stroke-width: 1px;
  -webkit-text-stroke-color: black;
}

Dies vermeidet Pseudoelemente (was für Bildschirmleser von Vorteil ist) und Textschatten (die unordentlich aussehen und dennoch einen leichten Sprungeffekt erzeugen können) oder das Festlegen fester Breiten (was unpraktisch sein kann).

Außerdem können Sie ein Element so einstellen, dass es mutiger als 1 Pixel ist (theoretisch können Sie eine Schrift so fett machen, wie Sie möchten, und es kann auch ein schlechtes Training sein, um eine fette Version einer Schrift zu erstellen, die nicht fett ist Variante, wie benutzerdefinierte Schriftarten ( Bearbeiten: Variable Schriftarten lehnen diesen Vorschlag ab ). Dies sollte jedoch vermieden werden, da dadurch einige Schriftarten wahrscheinlich kratzig und gezackt erscheinen.)

Ich dies funktioniert definitiv in Edge, Firefox, Chrome und Opera (zum Zeitpunkt der Veröffentlichung) und in Safari ( bearbeiten: @Lars Blumberg danke für die Bestätigung ). Es funktioniert NICHT in IE11 oder niedriger.

Beachten Sie auch, dass das -webkitPräfix verwendet wird, sodass dies kein Standard ist und die Unterstützung möglicherweise in Zukunft eingestellt wird. Verlassen Sie sich also nicht darauf, dass dies wirklich wichtig ist. Vermeiden Sie diese Technik am besten, es sei denn, sie ist lediglich ästhetisch.

Oliver
quelle
1
Funktioniert auch unter Safari 13.0.5. Die bisher sauberste Lösung für mich.
Lars Blumberg
4

Leider besteht die einzige Möglichkeit, eine Änderung der Breite zu vermeiden, wenn der Text fett gedruckt ist, darin, die Breite des Listenelements zu definieren. Wie Sie jedoch angegeben haben, ist dies manuell zeitaufwändig und nicht skalierbar.

Das einzige, was ich mir vorstellen kann, ist die Verwendung von Javascript, das die Breite der Registerkarte berechnet, bevor sie fett gedruckt wird, und dann die Breite anwendet, während die Fettschrift erforderlich ist (entweder wenn Sie den Mauszeiger bewegen oder klicken).

ajcw
quelle
hm, ich werde damit experimentieren, obwohl ich möchte, dass es für Nicht-JS-Benutzer zumindest vernünftig (dh fett gedruckt) aussieht. Vielleicht kann ich es fett senden, JS verwenden, um es aufzuheben, die Breite zu berechnen und es dann erneut fett zu schreiben? Oder ist das nur albern?
Mala
Ja, das klingt nach der besten Möglichkeit, Nicht-Javascript-Benutzer unterzubringen.
Ajcw
2

Verwenden Sie JavaScript, um eine feste Breite des Li basierend auf dem nicht fettgedruckten Inhalt festzulegen, und fetten Sie den Inhalt dann durch Anwenden eines Stils auf das <a>Tag (oder fügen Sie einen Bereich hinzu, wenn der <li>keine untergeordneten Elemente hat ).

Dan bläst
quelle
Ups - Entschuldigung für die Wiederholung: $
Dan Blows
Trotzdem danke für deine Antwort :)
Mala
@ Mala Hast du eine Lösung gefunden? Lustigerweise habe ich ein ähnliches Problem.
Dan Blows
1

Dies ist eine sehr alte Frage, aber ich besuche sie erneut, weil ich dieses Problem in einer App hatte, die ich entwickle, und alle Antworten hier als fehlend empfunden habe.

(Überspringen Sie diesen Absatz für die TL; DR ...)Ich verwende den Gotham-Webfont von cloud.typography.com und habe Schaltflächen, die hohl beginnen (mit einem weißen Rand / Text und einem transparenten Hintergrund) und beim Schweben eine Hintergrundfarbe erhalten. Ich stellte fest, dass einige der von mir verwendeten Hintergrundfarben keinen guten Kontrast zum weißen Text bildeten. Daher wollte ich den Text für diese Schaltflächen in Schwarz ändern, aber - ob aufgrund eines visuellen Tricks oder gängiger Anti-Aliasing-Methoden - dunkel Text auf hellem Hintergrund scheint immer leichter zu sein als weißer Text auf dunklem Hintergrund. Ich fand heraus, dass das Erhöhen des Gewichts von 400 auf 500 für den dunklen Text fast genau das gleiche "visuelle" Gewicht beibehielt. Es vergrößerte jedoch die Tastenbreite um einen winzigen Betrag - einen Bruchteil eines Pixels -, aber es reichte aus, um die Tasten leicht "zittern" zu lassen, was ich loswerden wollte.

Lösung:

Offensichtlich ist dies ein wirklich heikles Problem, daher war eine heikle Lösung erforderlich. Letztendlich habe ich ein Negativ letter-spacingfür den kühneren Text verwendet, wie oben empfohlen, aber 1px wäre viel übertrieben gewesen, also habe ich genau die Breite berechnet, die ich benötigen würde.

Bei der Überprüfung der Schaltfläche in Chrome devtools stellte ich fest, dass die Standardbreite meiner Schaltfläche 165,47 Pixel und 165,69 Pixel beim Schweben betrug, was einer Differenz von 0,22 Pixel entspricht. Die Schaltfläche hatte 9 Zeichen, also:

0,22 / 9 = 0,024444px

Durch die Umrechnung in em-Einheiten konnte ich die Anpassung der Schriftgröße unabhängig machen. Meine Schaltfläche hatte eine Schriftgröße von 16px, also:

0,024444 / 16 = 0,001527em

Also für meine bestimmte Schriftart, hält die folgende CSS die Tasten genau die gleiche Breite auf schweben:

.btn {
  font-weight: 400;
}

.btn:hover {
  font-weight: 500;
  letter-spacing: -0.001527em;
}

Mit ein wenig Testen und Verwenden der obigen Formel können Sie genau den richtigen letter-spacingWert für Ihre Situation finden, und es sollte unabhängig von der Schriftgröße funktionieren.

Die einzige Einschränkung besteht darin, dass verschiedene Browser leicht unterschiedliche Subpixel-Berechnungen verwenden. Wenn Sie also diese OCD-Stufe mit subpixelgenauer Präzision anstreben, müssen Sie den Test wiederholen und für jeden Browser einen anderen Wert festlegen. Browser-bezogene CSS-Stile werden aus gutem Grund im Allgemeinen verpönt , aber ich denke, dies ist ein Anwendungsfall, bei dem dies die einzige Option ist, die Sinn macht.

dannymcgee
quelle
0

Interessante Frage. Ich nehme an, Sie verwenden float, oder?

Nun, ich kenne keine Technik, mit der Sie diese Schriftvergrößerung entfernen können, daher werden sie versuchen, in die erforderliche Mindestbreite zu passen - und eine Variation der Schriftstärke ändert diesen Wert.

Die einzigartige Lösung, die ich kenne, um diese Änderung zu vermeiden, ist eine, von der Sie sagten, dass Sie sie nicht wollen: Festlegen fester Größen auf li.

Davis Peixoto
quelle
0

Sie können dies wie das Hover-Menü "Shop by Department" von amazon.com implementieren. Es verwendet breite div. Sie können ein breites Div erstellen und den rechten Teil ausblenden

Andrus
quelle
0

UPDATE: Musste das B-Tag für den Titel verwenden, da in IE11 die Pseudoklasse i: after nicht angezeigt wurde, wenn ich Sichtbarkeit hatte: versteckt.

In meinem Fall möchte ich ein (benutzerdefiniertes) Eingabe-Kontrollkästchen / Radio an Beschriftungstext ausrichten, wobei der Text fett ist, wenn die Eingabe aktiviert ist.

Die hier bereitgestellte Lösung hat bei mir in Chrome nicht funktioniert. Die vertikale Ausrichtung von Eingabe und Beschriftung wurde durcheinander gebracht mit: Nachdem Pseudo-Klasse und -Margen dies nicht behoben haben.

Hier ist ein Fix, bei dem Sie keine Probleme mit vertikalen Ausrichtungen haben.

/* checkbox and radiobutton */
label
{
    position: relative;
    display: inline-block;
    padding-left: 30px;
    line-height: 28px;
}

/* reserve space of bold text so that the container-size remains the same when the label text is set to bold when checked. */
label > input + b + i
{
    font-weight: bold;
    font-style: normal;
    visibility: hidden;
}

label > input:checked + b + i
{
    visibility: visible;
}

/* set title attribute of label > b */
label > input + b:after
{
    display: block;
    content: attr(title);
    font-weight: normal;
    position: absolute;
    left: 30px;
    top: -2px;
    visibility: visible;
}

label > input:checked + b:after
{
    display: none;
}

label > input[type="radio"],
label > input[type="checkbox"]
{
    position: absolute;
    visibility: hidden;
    left: 0px;
    margin: 0px;
    top: 50%;
    transform: translateY(-50%);
}

    label > input[type="radio"] + b,
    label > input[type="checkbox"]  + b
    {
        display: block;
        position: absolute;
        left: 0px;
        margin: 0px;
        top: 50%;
        transform: translateY(-50%);
        width: 24px;
        height: 24px;
        background-color: #00a1a6;
        border-radius: 3px;
    }

    label > input[type="radio"] + b
    {
        border-radius: 50%;
    }

    label > input:checked + b:before
    {
        display: inline-block;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%) rotate(45deg);
        content: '';
        border-width: 0px 3px 3px 0px;
        border-style: solid;
        border-color: #fff;
        width: 4px;
        height: 8px;
        border-radius: 0px;
    }

    label > input[type="checkbox"]:checked + b:before
    {
        transform: translate(-50%, -60%) rotate(45deg);
    }

    label > input[type="radio"]:checked + b:before
    {
        border-width: 0px;
        border-radius: 50%;
        width: 8px;
        height: 8px;
    }
<label><input checked="checked" type="checkbox"/><b title="Male"></b><i>Male</i></label>
<label><input type="checkbox"/><b title="Female"></b><i>Female</i></label>

Erik
quelle