Isolated styled-components with #font-face - styled-components

I'm using https://github.com/styled-components/styled-components.
I'm trying to work out the best strategy for components that require #font-face. I want to make sure each component is independent of its context, so I'm defining font-family styles on each them. But if I use injectGlobal in multiple components, I get multiple #font-face rules for the same font.
Should I just define the #font-face rules in my ThemeProvider entry-point component and live with the fact that the desired font might not be loaded by the browser?

That's exactly why we made injectGlobal. If you take a look at our docs they say:
We do not encourage the use of this. Use once per app at most, contained in a single file. This is an escape hatch. Only use it for the rare #font-face definition or body styling.
So what I'd do is create a JS file called global-styles.js which contains this code:
// global-styles.js
import { injectGlobal } from 'styled-components';
injectGlobal`
#font-face {
font-family: 'My custom family';
src: url('my-source.ttf');
}
`
As you can see, all we do here is inject some global styling-in this case #font-face. The last thing necessary to make this work is to import this file in your main entry point:
// index.js
import './global-styles.js'
// More stuff here like ReactDOM.render(...)
This will mean your font-face is only injected once, but still all of your components have access to it with font-family: 'My custom family'!
Doing it this way will give you a flash-of-invisible-text (FOIT), but that has nothing to do with styled-components-it'd be the same if you were using vanilla CSS. To get rid of the FOIT requires a more advanced font loading strategy rather than just #font-faceing.
Since this has nothing to do with styled-components, I highly recommend watching these two Front End Center episodes to learn more about how to best do this: "Crafting Web Font Fallbacks" and "The Fastest Webfont Loader in the West"!

And on the other side of the same coin in Chrome:
do not use #font-face inside injectGlobal if using e.g. react-router.
You will get re-paint of all of you app on each newly loaded route.
And this is why:
Same font-files downloaded on each new route.
As soon as you include font-face in a separate .css file - problem solves as stated in the last comment in this github issue.

injectGlobal is deprecated. Use createGlobalStyle
import { createGlobalStyle } from 'styled-components'
export const GlobalStyle = createGlobalStyle`
body {
font-family: 'Muli', sans-serif;
h1 {
font-weight: 800;
font-size: 36px;
p {
font-size: 18px;
}
}
`;
Then you can use it in your App.js:
import { GlobalStyle } from './styles/global'
class App extends Component {
render() {
return (
<ThemeProvider theme={theme}>
<>
<GlobalStyle/>
<Container/>
</>
</ThemeProvider>
)
}
}

I agree
I get reloaded with
import { createGlobalStyle } from 'styled-components';
import { silver } from 'shared-components/themes/colors';
export default createGlobalStyle`
#font-face {
font-family: "Proxima Nova";
font-style: normal;
font-weight: 300;
font-display: swap;
src: url("/static/media/fonts/proxima_nova/ProximaNova_300.otf");
}
and react create app

Related

How to use wavesurfer.js with a node bundler?

I am trying to use wavesurfer.js with node bundler, as indicated here:
https://www.npmjs.com/package/wavesurfer.js?activeTab=readme
but I can not get it working, I get the following error:
Uncaught SyntaxError: The requested module './../node_modules/wavesurfer.js/dist/wavesurfer.js' does not provide an export named 'default' (at TwoPlayers.js:2:9)
(I tried to import a module that I build without any troubles..., It seems like wavesurfer.js is not prepared to be load with import??)
Mi source code is:
import { LitElement, html, css } from "lit-element";
import WaveSurfer from 'wavesurfer.js';
import { MyFunction2 } from './demo'
// import * as WaveSurfer from 'wavesurfer.js';
class TwoPlayers extends LitElement {
static styles = css`
:host {
display: block;
border-radius: 0.5rem;
padding: 0.2rem 0.5rem;
background-color: #475119;
color: #fff;
font-weight: bold;
}
`;
In the examples folder always is used as a library loaded in the HTML, but there aren't any example using node bundler.
Has anyone managed to get it working with node using import?
Thanks in advance
Using this way to import the package:
import 'wavesurfer.js';
It will work, and you can use WaveSurfer object
The documentation say to use:
import WaveSurfer from 'wavesurfer.js';
but it is not working

How to change static css file on API response in ReactJS

In my project i want to change the background-color and font of text. Both the properties are written in css file.
Project structure is:
|-myProject
|--public
|--src
|--package.json
All my css is written in public directory, and i have an api which give response of background-color and font. Now i want to change the properties background-color and font in css files according to api response.
Instead of trying to modify the base stylesheets, why not set these particular properties using the elements’ style attributes:
const divStyle = {
backgroundColor: /* Some color */,
fontFamily: /* Some font stack */,
};
function HelloWorldComponent() {
return <div style={ divStyle }>Hello World!</div>;
}
(adapted from the React docs)
I think the best way to do this would be to use inline style on the elements you want to change.
On api response -> set
const yourVar={
backgroundColor:##,
fontFamily:##
};
I believe that the answer from MTCoster is the best approach. depending on the structure of your app you could use the new Context API to make some sort of theme provider, so that you could pass custom styles that could be stored on your application state and that is loaded from your backend API. there are some tools that could help you integrate this feature more easily, like Styled-Components.
with Styled components you culd write something like:
import styled from 'styled-components'
import { YourComponentJSX } from '../somewhere'
// Wrap the component where you need your custom styles
const YourStyledComponent = styled(YourComponentJSX)`
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border-radius: 3px;
/* Color the border and text with theme.main */
// using the short-if to add a default color in case it is not connected to the ThemeProvider
color: ${props => props.theme.main ? props.theme.main : "palevioletred"};
border: 2px solid ${props => props.theme.main ? props.theme.main : "palevioletred"};
`;
// Define what props.theme will look like
const theme = {
main: "mediumseagreen"
};
render(
<div>
<ThemeProvider theme={theme}>
<App>
<YourStyledComponent>Themed</YourStyledComponent>
</App>
</ThemeProvider>
</div>
);
This way you could wrap your whole app and use custom styles saved on the app state that have been loaded from the backend and use them on really deeply nested ui components
*The code is a modification from the styled-component docs

chrome extension shadow DOM import boostrap fonts

so I'd like to display bootstrap 3 icons in a shadowroot added from a chrome extension content script. So far its not working, help?
manifest.js does include the woff file in web_accessible_resources
shadow root has style tag with:
#import url(chrome-extension://__MSG_##extension_id__/fonts/glyphicons-halflings-regular.woff2);
What am I missing?
To import a font, you should not use #import url which is used to import a CSS stylesheet.
Instead, you should use the #font-face directive.
Also, this directive should be placed in the <head> element of the HTML page, not inside the Shadow DOM.
host.attachShadow( { mode: 'open' } )
.innerHTML = `
<style>.icon { font-family: Icons; color: green ; font-size:30pt }</style>
<span class="icon">&#xe084</span>`
#font-face {
font-family: "Icons" ;
src: url("https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff2") format("woff2")
}
<div id=host></div>
You can read this SO answer for further details.

Vuex & Websockets

So currently I am working with VueJS 2 and I am very new with it. Now I was getting some help with some other people, but I am still stuck.
Here is what I want to achieve (example - closely linked to what I want):
I have a NodeJS application that listens on WebSockets. The application listens for connections via WebSocket and will take JSON data, with a command and then a data object with any content needed for that command.
The command for example could be login, and the data be username and password. The login function on the NodeJS application will then take this data, do what it needs and then return it back over the socket, whether it was successful or not, and maybe include an ID and some user information for Vuex to pickup and place in it's state, for the front-end of the application to pick up/use.
Currently I am using this boiler plate: https://github.com/SimulatedGREG/electron-vue
Which has served me very well as a learning curve, due to me wanting to use Vue and Vuex to manage my application and then use WebSockets for managing data to and from the data server.
So if you look at the link I sent in app/src/renderer/ (this is where the main code is for vue and vuex).
A friend of mine added the following code for me as an example and I am stuck trying to get it into vuex as actions and mutations. He made it all in one vue component, so I am struggling on how it works with vuex. As I want to be able to access the (example) loginUser action from anywhere in the application (uses routes for multiple pages/views).
So in the MyApp/app/src/renderer/components/LandingPageView.vue
<template>
<div>
<img src="./LandingPageView/assets/logo.png" alt="electron-vue">
<h1>Welcome.</h1>
<current-page></current-page>
<websocket-output></websocket-output>
<versions></versions>
<links></links>
</div>
</template>
<script>
import CurrentPage from './LandingPageView/CurrentPage'
import Links from './LandingPageView/Links'
import Versions from './LandingPageView/Versions'
import WebsocketOutput from './LandingPageView/WebsocketOutput'
export default {
components: {
CurrentPage,
Links,
Versions,
WebsocketOutput
},
name: 'landing-page'
}
</script>
<style scoped>
img {
margin-top: -25px;
width: 450px;
}
</style>
That is the updated file for that, and then below is the code for the MyApp/app/src/renderer/components/LandingPageView/WebsocketOutput.vue
<template>
<div>
<h2>Socket output:</h2>
<p v-text="socket_out"></p>
</div>
</template>
<script>
var ws = require("nodejs-websocket")
export default {
data () {
return {
socket_out: "connecting to the websocket server..."
}
},
mounted () {
const parent = this
var connection = ws.connect("ws://dannysmc.com:9999", {}, function (conn) {})
connection.on("text", function (text) {
console.log('Text received: ' + text)
parent.socket_out = text
})
connection.on("connect", function () {
connection.send('yo')
})
},
created () {
// Set $route values that are not preset during unit testing
if (process.env.NODE_ENV === 'testing') {
this.$route = {
name: 'websocket-output',
path: '/websocket-output'
}
}
}
}
</script>
<style scoped>
code {
background-color: rgba(46, 56, 76, .5);
border-radius: 3px;
color: #fff;
font-weight: bold;
padding: 3px 6px;
margin: 0 3px;
vertical-align: bottom;
}
p {
line-height: 24px;
color: red;
}
</style>
Everything else is just the boiler plate that you see, so if anyone is willing to help me and give me some tips of what to read that explains this or anything else? as I can't find much information on it unfortunately.
I have an electron application that uses Vue and a websocket for information and here is how I set mine up.
I have a store defined that also actually creates and sets up the websocket.
Store.js
const socket = require("socket-library") // Take your pick of socket libs
const mySocket = new socket(...)
mySocket.on("message", message => store.handleMessage(message))
...other handlers...
const store = {
handleMessage(message){
// do things with the message
}
}
export default store
Renderer.js
import store from "./store"
new Vue({
data:{
store
}
})
This exposes my store at the root level of my Vue and allows me to pass data to components, or what have you. The store manages all the incoming information from the websocket.
With you wanting to use Vuex, you could do essentially the same thing, where Vuex would be your store and when messages come in over the socket, you just pass them to Vuex.
mySocket.on("message", msg => vuexStore.dispatch("onSocketMessage", msg))
and set up your Vue and components to work with Vuex as you typically would.

Node.JS Vash TypeError: while rendering template object is not a function

There is another thread that covers this, but I am not allowed to post to it. Also, the only answer does not seem to solve my problem.
I am getting the Object not a function error when using the #html.extend() method. I have read all of the very limited threads on this topic. They all say the same thing. That I need to ensure the path is correct to the layout.vash file I am extending. My declaration looks like this in the file that I want to want to extend with my layout.vash file.
#html.extend('layout', function (model) {
.... do stuff ...
})
What is odd, is that some pages work fine others don't. The path is correct. I am sure of this because of the fact the files in the same director exhibit different behavior.
Does anyone know what other mistake I could be making to cause this error?
In my case, vash was unable to parse the content within ...
I pulled it out from the layout page and created a separate .css file, and the annoying "object is not a function" error disappeared.
I speculate that vash collides with some css syntax.
For you info, my style statements that caused the trouble were these.
<style type="text/css">
*{padding:0;margin:0;}
html{border-top:10px #1abf89 solid;}
body{width:800px;margin:0 auto;padding:5% 20px 20px;font-family:Palatino, Optima, Georgia, serif;}
#media all and (max-width:1024px){ body, pre a{width:60%;} }
small{color:#999;}
#toolbar{margin-bottom:1em;position:fixed;left:20px;margin-top:5px;}
#toolbar [class^="icon-"]:before, #toolbar [class*=" icon-"]:before{font-family:'pen'}
#mode{color:#1abf89;;cursor:pointer;}
#mode.disabled{color:#666;}
#mode:before{content: '\e813';}
#hinted{color:#1abf89;cursor:pointer;}
#hinted.disabled{color:#666;}
#hinted:before{content: '\e816';}
#fork{position:fixed;right:0;top:0;}
/*
When the webpage is printed
this media query hides extra elements,
and makes the text content fit the page.
*/
#media print {
#fork, #toolbar {
display: none;
}
body {
width: 94%;
padding-top: 1em;
font-size: 12px;
}
html {
border-top: 0;
}
}
</style>

Resources