I have some UIFonts like this:
let regularFont = UIFont.systemFont(ofSize: 18, weight: .regular)
let mediumFont = UIFont.systemFont(ofSize: 18, weight: .medium)
I use them in my UIKit components like UILabel.
Now I want to render some text in a WKWebView using the same fonts.
I generate some html template in code but I can't figure out how to setup the css font styles to match my given UIFonts.
With some guesswork I came up with the following which kind of works for my given examples:
<html>
<head>
<style>
body {
font-family: '.AppleSystemUIFont';
font-size: 18.0;
font-weight: normal;
}
b {
font-family: '.AppleSystemUIFont';
font-size: 18.0;
font-weight: bolder;
}
</style>
</head>
<body>
<p>
Regular font <b>Medium font</b> Regular font
</p>
</body>
</html>
It works ok-ish but clearly this does not scale (how about other weights?).
Also I can already see my designer coming at me, claiming that font weight is off by .1 or something.
This is my current effort. It works on a limited subset of properties (bold, italic) but it does not handle font weight. Also I haven't tested for custom fonts yet but my guess is it won't work like that.
extension UIFont {
func cssStyleString(color: UIColor) -> String {
let colorString = "rgb( \(CIColor(color: color).red * 255), \(CIColor(color: color).green * 255), \(CIColor(color: color).blue * 255) )"
// not sure this is right
let fontFamily = self.fontName == self.familyName
let fontSize = self.pointSize
// trait is not quite right for translation into weight. suggestions?
let fontWeight = self.fontDescriptor.symbolicTraits.contains(.traitBold) ? "bold" : "normal"
// works for italic only.
let fontStyle = self.fontDescriptor.symbolicTraits.contains(.traitItalic) ? "italic" : "normal"
return "font-family: '\(fontFamily)'; font-size: \(fontSize); font-weight: \(fontWeight); font-style: \(fontStyle); color: \(colorString); "
}
}
So hence my question:
What is the best way to create css styles from a given UIFont, especially with respect to font weight?
Related
I'm currently in the process of updating all my websites from using webfonts to hosting the fonts locally by myself. This process is a little bit frustrating, because I often can't find the css classes of the webfonts. At the moment, it's more a "try and error" kind of thing, where I'm just klicking trough the google chrome dev tools and looking for the corresponding css classes. So I was wondering if there is a simple way to look in a published website via browser for the css classes of a specific font family? (I cannot search for the classes in the IDE, because in this use case the websites where developed with webflow)
EDIT: The websites in question were created with a "building block" system called "Webflow". There, the fonts are selected via graphical interfaces. Now the problem is that somewhere in these old and huge web pages there are CSS classes that use the "Lato webfont". I want to replace this font, but I can't search for used fonts in this graphical interface. What I can search for are the CSS classes. So my idea was to use the Chrome Dev Tools to find out which CSS classes used the Lato font to ultimately replace it.
Find css rules by properties
If you can't edit you site's css files globally you might at least get some sort of "cheat sheet" containing all selectors matching certain property values.
let cssRules = getCssRules();
let filterLato400 = findRulesByProperties(cssRules, {
"font-family": "Lato",
});
console.log(filterLato400);
let filterLato400Italic = findRulesByProperties(cssRules, {
"font-family": "Lato",
"font-weight": 400,
"font-style": "italic"
});
console.log(filterLato400Italic);
//get all css rules in document
function getCssRules() {
let cssText = "";
let rules = [
...(document.styleSheets[0].rules || document.styleSheets[0].cssRules)
];
let cssArr = [];
rules.forEach(function(rule) {
let selector = rule.selectorText;
let cssText = rule.cssText;
if (selector && cssText) {
let properties = cssText
.replace(selector, "")
.replace(/[{}]/g, "")
.split(";")
.map((val) => {
return val.trim();
})
.filter(Boolean)
.map((vals) => {
return vals.split(":");
});
cssArr.push({
selector: selector,
properties: properties
});
}
});
return cssArr;
}
//filter css rules by properties
function findRulesByProperties(css, filters) {
let classList = [];
css.forEach(function(rule) {
let selector = rule.selector;
let props = rule.properties;
let vals = [];
let valsFilter = [];
for (let key in filters) {
let filterName = key;
let filterValue = filters[key];
valsFilter.push(filterValue.toString());
}
for (let i = 0; i < props.length; i++) {
let prop = props[i];
let propName = prop[0];
let propValue = prop[1].trim();
if (valsFilter.indexOf(propValue) != -1) {
vals.push(propValue);
}
}
if (vals.length == valsFilter.length) {
classList.push(selector)
}
});
return `results ${classList.length}: ${classList.join(", ")} || match: ${JSON.stringify(filters)}`;
}
body {
font-family: Georgia;
}
h1 {
font-family: "Lato";
font-weight: 700;
}
h2 {
font-family: "Lato";
font-weight: 400;
}
.classLato400 {
font-family: "Lato";
font-weight: 400;
}
.classLato400italic {
font-family: "Lato";
font-weight: 400;
font-style: italic;
}
.classLato700 {
font-family: "Lato";
font-weight: 700;
}
.classRoboto400 {
font-family: "Roboto";
font-weight: 400;
}
In the above example we're searching for all rules containing font-family:Lato (and other filters like font-weight or font-style).
You could paste your main css file in the snippet to get a list of selectors matching all criteria.
Replace external #font-face rules
If I got you right and your ultimate goal is to replace externally hosted font files with local ones (e.g. to improve GDPR compliance), you don't need to get every css font-family class reference.
The most important part are the #font-face rules that are actually responsive for downloading font files.
OK that's not perfectly correct since a font file won't be downloaded unless some DOM element uses this particular font-family.
In other words, your css might actually contain a plethora of unused font-families – on the other hand if they aren't used anywhere they won't be downloaded (so browsers have a default lazyloading method for fonts).
Example: you need to replace google webfonts with locally hosted fonts
Open your devTools and switch to the "Font" tab
Now you can see a list of all downloaded font files as well as their origin (URL) and their "Initiator" – the source file, that initiated the file download. Usually this would be a <link> stylesheet reference or an #import rule within your css, but it can also be a javaScript font loader method.
By inspecting the "URL" column, we can clearly see if a font is loaded from an external host.
Clicking the "Initiator" row/entry will open the file triggering the download – this will either be a file (like a .css) you want to completely remove or just a portion of a css file (take a closer look at #font-face rules, especially the src properties).
Following the google webfonts use case/example
(actually pretty similar to other font delivery services)
obviously we need to get local copies of my previously externally hosted font files –
google web font helper might be helpful to get a ready-to-go #font-face css and a download package including all needed font files.
we need to delete all css files or #font-face or #import rules that are still referring to external file sources and replace them with custom local font file urls.
Possible shortcuts to remove external font file references:
Check your HTML/template head for undesired elements like these
(so containing an external URL like "fonts.googleapis.com"):
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght#400;700" rel="stylesheet">
or within inline <style> tags for #import rules like #import url('https://fonts.googleapis.com/css2?family=Roboto:wght#400;700')
or similar #import rules within your main css file – they should usually be found at the top of your css code.
Delete these references and replace with custom #font-face rules like so (example is based on google web font helper output using "Roboto" font-family and font-weights 400+700 ... regular and bold).
/* roboto-regular - latin */
#font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url('fonts/roboto-v30-latin-regular.woff2') format('woff2');
}
/* roboto-700 - latin */
#font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: url('fonts/roboto-v30-latin-700.woff2') format('woff2');
}
Inspect the network tab once again
If everthing is working fine we should see the locally retrieved font files for each style (e.g. regular, bold, italic, bold italic etc.)
If not: double check your file paths!
Seriously, this is probably the most common source of errors. (e.g "../fonts/" or "./fonts/" or just "fonts/").
I am Using a Regional language unicode font-face in my site but the numbers are not looking good.
So I want to apply new font-style or css to numbers only..
please help
This can be done using CSS's unicode-range property which exists within #font-face.
The numbers 0 to 9 exist in Unicode within the range U+0030 to U+0039. So what you'll need to do is include a font alongside your existing font which specifically targets this range:
#font-face {
font-family: 'My Pre-Existing Font';
...
}
#font-face {
font-family: 'My New Font Which Handles Numbers Correctly';
...
unicode-range: U+30-39;
}
The result of this will be that every instance of Unicode characters U+0030 (0) through to U+0039 (9) will be displayed in the font which specifically targets that range, and every other character will be in your current font.
You can wrap all numbers in p tags with a <span class="number">:
CSS
.number {
font-family: Verdana;
}
jQuery
$('p').html(function(i, v){
return v.replace(/(\d)/g, '<span class="number">$1</span>');
});
But personally, I would go with James suggestion ;)
http://jsfiddle.net/ZzBN9/
There is no way to apply CSS to all numbers specifically. In each number tag you could add the attribute class='number' and then in the CSS you could add
.number {
font-family: arial;
}
Better with this
$('p').html(function(i, v){
return v.replace(/(\d+)/g, '<span class="number">$1</span>');
});
With + you avoid one span per complete number (span for 321), not one per each number found (span for 3 for 2 and for 1)
You can use the regex replace and detect the numbers then add the class
following code:
$('p').html(function(i,c) {
return c.replace(/\d+/g, function(v){
return "<span class='numbers'>" + v + "</span>";
});
});
.numbers
{
color:red;
font-size:30px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>
View 11 new out of 11 message(s) in your inbox
</p>
I'm working on an online store project and it needs a custom font. I'm using Materialize CSS framework in Angular 7, but btn class doesn't apply correctly custom font, anything else does. any help please friends!
I have tried custom btn class apply to style with !important, it does apply but damaged ways.
#font-face {
font-family: 'AcadNusx';
src: url('./assets/fonts/AcadNusx.woff') format('woff2'),
url('./assets/fonts/AcadNusx.woff2') format('woff');
font-weight: normal;
font-style: normal;
}
body {
font-family: 'AcadNusx' !important;
padding: 0;
margin: 0;
}
a {
font-family: 'AcadNusx' !important;
}
.btn {
font-family: 'AcadNusx' !important;
}
and the template code is:
registracia
when I delete btn class from the button font apply correctly but there is no styling
please help, I need just to apply this font but btn style is making me headache. thanks for your attention.
best regards
AcadNusx is an 'unusual' Georgian typeset _
To display it correctly needs more #font-face information than you currently have in your CSS code _
According to the Fonts2U resource_ you need to upload (or install via Angular cli?) all 4 versions of the font to your fonts folder_ including .ttf,.woff,.eot,.svg _ then copy / paste the accompanying CSS code into your main CSS file or import the code as separate CSS _
The 4 versions of the font_ the CSS code_ and all the instructions you need_ are all included in the downloadable resource from: https://fonts2u.com/acadnusx.font
I use Bold, Medium and Normal font weights on my website, that's 700, 500 and 400 respectively.
I use Helvetica Neue font and as a fallback for systems that doesn't have it installed I want to use Open Sans. The problem is Open Sans doesn't have Medium style.
I want my elements that I used to define as font-weight: 500 have font-weight: 600 if the browser uses Open Sans. Is it possible somehow?
There's a similar question at Stack Overflow: How to set different font-weight for fallback font? but I'cant get the result I need using techniqe described in an accepted answer.
I need something like
#font-face {
font-family: 'semibold';
src: 'Helvetica Neue':500, 'Open Sans':600;
}
Not sure how to do it though.
You can't really define weight in a font-face declaration. Instead, font-weight is used there as a gatekeeper to match the font and not to pass styles to the element.
It seems like overkill, but you could use this JavaScript function by Sam Clarke as a starting point to see if the font is available, and then conditionally modify the font-weight following the logic that works best for your specific requirements.
For a simplified example with just these two fonts, you might set up the CSS like this:
#font-face {
font-family: h-semibold;
src: local('Helvetica Neue');
}
#font-face {
font-family: os-semibold;
src: local('Open Sans');
}
.semibold {
font-family: h-semibold, os-semibold;
}
.w5 {
font-weight: 500;
}
.w6 {
font-weight: 600;
}
Then, using the function linked above, you put something like this in your JS to conditionally load the weight classes depending on font support:
var semibold = document.querySelectorAll('.semibold');
if (isFontAvailable('h-semibold')) {
semibold.forEach(result => {
result.className += ' ' + 'w5';
});
} else {
semibold.forEach(result => {
result.className += ' ' + 'w6';
});
}
You'll doubtless work out a more elegant solution if you really need to carry it through.
Is it possible to fake lowercase letters on a font that has only one letter type, which is ALL CAPS?
This is a sentence on Stack Overflow.
Looks like this when the font is applied:
THIS IS A SENTENCE ON STACK OVERFLOW.
I want the capitals to be a slightly larger font size as in the example below. But without the additional HTML markup.
body {
font-family: Arial, sans-serif;
font-size: 12px;
}
span {
font-size: 1.4em;
}
<span>T</span>HIS IS A SENTENCE ON <span>S</span>TACK <span>O</span>VERFLOW
I'd say the best way to achieve this is with JavaScript (so you could keep the markup dynamic). The JS part that you'd need is this (remember to add .small-caps class to your text elements):
function smallCaps() {
var elements = document.querySelectorAll('.small-caps')
Array.prototype.forEach.call(elements, function(e) {
var text = e.innerHTML.toUpperCase()
e.innerHTML = text.replace(/\b([A-Za-z0-9])/g, '<span class="caps">$1</span>')
})
}
And also remember to add styles for the .caps class:
.caps {
font-size: 1.4em;
}
See it in action either in a fiddle or below:
smallCaps()
// This is what you need:
function smallCaps() {
var elements = document.querySelectorAll('.small-caps')
Array.prototype.forEach.call(elements, function(e) {
var text = e.innerHTML.toUpperCase()
e.innerHTML = text.replace(/\b([A-Za-z0-9])/g, '<span class="caps">$1</span>')
})
}
.caps {
font-size: 1.4em;
}
<h1 class="small-caps">HELLO WORLD</h1>
<h2 class="small-caps">Nifty FOOBAR title</h2>
Sentence case in font-variant: small-caps. The titleCase() function works perfectly with the letters wrapped in <span>s.
I want the capitals to be a slightly larger font size as in the example below. But without the additional HTML markup.
The first 4 comments on OP are correct. I'd like to reaffirm #Pete's comment:
css can only target the first letter or word in a sentence, other than that you will need extra html otherwise how would css know you want the first letter and then the s of stack and o of overflow?
Thus, you will get answers of every variety and each successful answer will have markup in some form or another. With JavaScript, you could have a range determined by a whitelist/dictionary but covering any range of proper nouns would be very limited. Capabilities of that magnitude should be possible with a language like Python, Java, C/C++, etc.
Demo
var main = document.body;
var text = main.textContent;
titleCase(text);
main.style.fontVariant = 'smallCaps';
function titleCase(str) {
return str.replace(/\w\S*/g, function(txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
}
body {
font-family: Arial, sans-serif;
font-size: 12px;
}
span {
font-size: 1.4em;
}
<span>T</span>HIS IS A SENTENCE ON <span>S</span>TACK <span>O</span>VERFLOW