Create a static div to hold an advertisement in a Compass Susy grid? - responsive-design

I have to accomodate an MREC advertisement in my layout. The ad is 300pixels wide, and cannot resize as the fluid grid otherwise contracts.
Further, the ad needs to be the first item on smartphone, before the headline block. So, on the breakpoint I am setting it to omega to push it "after" the headline, as in this screenshot (gray rules just to make it easier to see).
The headline is 8 cols, the ad is 4. All is fine except on ipad the ad column and the ad reduces to under 300 pixels, which we are not allowed to do. So, how to keep everything fluid except the ad container?

Set max/min widths on the container?

This is not hard to do, but it means the grid up top won't exactly match the flexible grid below. I assume that isn't an issue. You have to use some functions to lay out these two elements, but everything else can be done exactly as it was before.
You can keep the ad flexible down to a minimum width:
.ad {
#include span-columns(4 omega);
min-width: 300px; // you can use any width you want.
}
Or you can make the ad completely static:
.ad {
float: right;
width: columns-width(4); // you can use any width you want.
}
The important part is that you must not set a column width on the headline.
You have a few other options. The simplest might be to set right margins and padding equal to the static ad size (plus gutter):
.headline {
margin-right: columns-width(4) + $gutter-width;
}
Or, if you want that gutter to flex, try:
.headline {
margin-right: columns-width(4);
padding-right: gutter();
}
You can add clear: both; to the main content to make sure it clears the headline and ad.
If this approach doesn't work for you, try creating a new "formatting context" for the headline. One of the classic techniques is simply overflow: hidden;. Nicole Sullivan has a good blog post on how they do it for oocss, but it gets a bit more complex and you may not need all that.
UPDATE:
All these solutions require the ad coming first in the markup. The only way around that is if you know the height of the ad. In that case, you could position the ad absolutely rather than floating it, create space for it in the same way, and set a min-height on the headline (or row-container if you have one).

Related

How do 'nubs' work in Bot Framework React Webchat?

I am aiming to make messages in a Microsoft Bot Framework Chatbot appear more like 'speech bubbles' and have a triangular 'nub' display to the side of each, eg
The React Webchat client provided as part of the Bot Framework has optional properties to control the size and position of nubs which are
bubbleNubOffset?: number | 'bottom' | 'top';
bubbleNubSize?: number;
My first question is: Regardless of what (positive) bubbleNubOffset value is used, the nub always appears at the top of the message bubble rather than being offset eg
How can I move the nub down to appear more like the first image? Setting bubbleNubOffset to any negative number moves the nub to the bottom of the message bubble.
The source in GitHub for them has a comment
/**
* Nub offset ''bottom' will render nub at the bottom
* A positive or negative number will shift nub offset up/down
* "top" is equivalent to positive zero.
* "bottom" is equivalent to negative zero.
*/
but it doesn't appear to actually work like that.
My second question is: Without adding CSS to set the z-index of the message element, setting the bubbleNubSize causes the nub to be displayed in front of the message eg:
Is there a way to avoid this via configuration or is custom CSS the only way to do it?
My guess is you are supplying too large of a value to the bubbleNubSize property. For instance, here are the values that I use in my test Web Chat client.
bubbleBackground: 'black',
bubbleBorderColor: 'red',
bubbleBorderRadius: 9,
bubbleBorderWidth: 2,
bubbleTextColor: 'white',
bubbleNubSize: 5,
bubbleNubOffset: 'bottom'
bubbleFromUserBackground: 'black',
bubbleFromUserBorderColor: 'green',
bubbleFromUserBorderRadius: 9,
bubbleFromUserBorderWidth: 2,
bubbleFromUserTextColor: 'white',
bubbleFromUserNubSize: 5,
bubbleFromUserNubOffset: 'top'
Thankfully, Web Chat has built-in logic that helps interpret how the bubble and nub displays based on the values you provide. For instance, the nub mirrors vertically depending on the offset, and the border radius of the nub corner to be used is set to 0 to create a visually seamless flow.

Inner section in Elementor issue for phone screen

enter image description here
enter image description here
Hello, I have an issue. I did add 2 inner sections in Elementor which contain 3 columns. For desktop, it looks great but for the phone, I cannot make it into 2 columns. Anybody can advise me, please?
You'll have a hard time doing this if your content is split between different inner sections.
Here's how you could do it with a little CSS.
(1) Create a new section with just one column, and add all your content to this one column. For illustration, I'm using paragraphs but the same principle will apply for other content.
(2)Go to section settings > advanced > advanced, and give this section a class of 'item-wrap'
(3) Go to section settings > advanced > custom CSS and add the following
/* set default widget width to 50% */
.item-wrap .elementor-widget-wrap>.elementor-element {
width: 50%;
}
/* set widget width to 33.33% on tablet and desktop */
#media(min-width:768px){
.item-wrap .elementor-widget-wrap>.elementor-element {
width: 33.33%;
}
}
(4) Here's the end result. Depending on the specifics of your page, we may need to tweak the CSS selectors in step 3. Give this a try and let me know how it goes.

How to handle Vue 2 memory usage for large data (~50 000 objects)

I'm trying to implement an table-view for large collections of semi-complex objects on Vue 2. Basically the idea is to collect anywhere between 50 000 to 100 000 rows from DB into JS cache, which is then analyzed dynamically to build table-view with real-time-filters (text-search). Each row within table is toggleable, meaning that clicking the row changes the row to edit-mode, which enables Excel-like editing for that specific field/cell.
Each object has about ~100-150 fields/properties, but only certain amount of 'em are shown at any given moment within table (table columns can be toggled in real-time). For large datasets it seems that DB is pushing about ~10-100mb of JSON-data, which in this use-case is acceptable. Renderwise the performance isn't an issue -- filters work fast enough and only limited amount of results are rendered to DOM.
Everything already works, filters, listing ~100 rows against filters (+ "show 100 more"-mechanism etc), but I hit memory limit when I loaded around 8000 objects into array. This seems to reserve 2 gigabytes of RAM, which after Chrome stops running JS-code all together (even though strangely I'm not getting any kind of warning/error).
I benchmarked memory usage for rows and it seems that ~1000 rows reserves around 300mb of memory. This is most likely reserved by Vue reactivity observers.
Three questions:
Is there a way to toggle reactivity for specific array-list objects (by index or such), so that objects within array itself are unobserved/non-mutable unless specifically called to become mutable (ie. when user clicks row, which enables edit-mode)?
How would you implement handling of large datasets for Vue, as reactivity seems to bottleneck the memory usage? Please do not suggest "limit the results within backend", because it's not the solution which I'm seeking here (even though I may need to create two-part filtering, one for fetching smaller initial dataset and one for realtime filtering). Basically I'm trying to push boundaries of "end of memory" from 8 000 -> 80 000 by re-thinking the data-architecture with Vue. Is the only problem having dataset storaged within Vue's data-variables as reactive?
One idea I have is to turn that "items" -dataset to non-observable/non-reactive with Object.freeze or some similar approach and have table to render two datasets: one for non-reactive and one for those which are currently within edit-mode (which would be pushed to "editableItems" dataset when row is clicked)... any suggestions here (anything simpler, so that I'm able to handle everything within one array?)
I have done similar application on Angular 1, and it handled 50 000 rows quite well, so I'm sure it should be doable within Vue 2 as well... should be just a matter of finding a way on handling reactivity.
Edit 20.4.2021 - Two years later, two years wiser
As this question/answer has gotten lots of attention and is still valid after all the years I wanted to throw few pointers. Most of the details underneath are is still valid. Still, I would direct towards using VueX with Lodash (or modern version of native JS functions) when dealing with filtered results & complex objects.
In order to ease the stress of your backend you can keep things simple: Fetch plain objects without related models. This means that your main results have only ID-keys to related objects. Use Axios or similar library to fetch all the related data with separate AJAX-requests ("customers", "projects", "locations") and use VueX to store 'em in their own list-properties. Create getters for each, such as:
projectsById: state => {
return _.keyBy(state.projects, "id")
},
This way you can use related models for fetching labels and/or full objects when required and your backend doesn't need to fetch related data more than once. States and getters will be available within micro-components as well.
Basically: Avoid fetching full model-trees (even though C# EF or PHP Laravel provide tools for 'em) when dealing with large datasets. Use atomic approach: Fetch 20 different lists ("Axios.all([...])" is your friend!), each with own controller endpoint and cache the results to VueX store... And have fun ;)
Edit 12.03.2019 - additional tips at the end of this answer
It's been a while since I asked this question and I finally got to optimize this part of my project. I'd like to give few pointers for anyone having these performance and/or memory-issues.
Vue documentation never really explained it, but as Andrey pointed out you CAN use the component-object as an data-storage for your custom objects & object-lists. After all, it's just an normal javascript-object.
After optimization my list component setup looks somewhat like this:
module.exports = {
items: [],
mixins: [sharedUtils],
data: function() {
return {
columns: {
all: []
etc... Lot's of data & methods
The items-array is filled with thousands of complex objects (about 80mb of data, 6mb compressed) which I'm handling as non-reactive. This proved to be less of an issue than I would have thought -- Instead of using v-for directly against items I was already using structure in which I triggered filtering of this array whenever user clicked some filter-button and/or inputted string-filtering (such as name). Basically this "processFilters"-method goes through non-responsive items-array and returns filteredItems, which is stored in data-context. Thus it automatically becomes reactive as it's mutated.
<tr v-for="item in filteredItems"
This way all the items within filteredItems stay reactive, but also lose reactivity when they are filtered out, thus saving bunch-load of memory. Whopping 1200mb shrunk to 400mb, which was exactly what I was looking for. Clever!
There are few issues which need to be addressed. Since items doesn't exist in data-context you cannot use it directly within template. This means that instead of writing...
<div v-if="items.length > 0 && everythingElseIsReady">
... I had to store length of items-array to separate data prop. This could have been fixed with computed value as well, but I like to keep those properties existing.
Giving up the reactivity of your main data-array isn't such a bad thing after all - The most important part is to understand that modifications which are made directly against items within that base-array are never triggering any changes to UI and/or sub-components (douh). This shouldn't be such an issue as long as you separate your code in such a way that you have "hidden data container" which holds all the results from backend, and you have smaller (filtered) presentation array of that large container. By using good REST-architecture you should already be good to go with non-reactive data-storage, as long as you remember to check that after saving the item within non-reactive data storage has also been updated to latest revision.
Additionally I was baffled by how little it matters performance-wise how many micro-components there are against hundreds of rows. The render takes a hit obviously, but even if I were to pass large props thousands of times (as I have thousands of instances of input-cells) it didn't seem to hit the memory. One of this kind of objects is my global translations-key/value-pair object, having over 20 000 lines of translated strings... but it still didn't matter. This makes sense, as Javascript uses object-references and Vue Core seems to be properly coded, so as long as you use such configuration objects as props you are simply referring from thousands of objects to the same data-set.
Finally, I'd say start going crazy with complex CRUD objects without fear of hitting memory limit!
Huge thanks for Andrey Popov for giving nudge towards right direction!
Tips (12.03.2019)
Since it's been a while and as I have continued building UI's with large & complex datasets I decided to drop few short ideas & tips.
Consider how you manage your master-records (ie. persons or products) vs related records (sub-objects / relational objects). Try to limit the amount of data injected for subcomponents, as you might be representing the same sub-object multiple times for different master-records. The problem is that it's possible that these objects are not actually reference-objects!
Consider situation where you have person-object, which contains city-object. Multiple persons live in the same city, but when you fetch JSON-data from backend are you sure are those duplicated city-objects actually one and same city (shared/referenced city-object between persons), or multiple representations of similar object (with data being exactly same, but under the hood each one being an individual instance / unique object). Let's say that you have 50 000 persons, each one containing the same sub-object/property "city": { id: 4, name: "Megatown" }, did you just fetch 50 000 individual city instances instead of just one? Is person1.city === person2.city , or do they just look the same and still be two different objects?
If you are unsure whether you are refering to shared city-object or using dozens of instances of similar sub-objects you could simply do there referencing inside your person-list-component. Your person contains city-id, so fetch list of cities with separate REST-method (getCities), and do the pairing on UI-level. This way you have only one list of cities, and you could resolve city from that that list and inject it to person, thus making reference to only one city. Alternatively you could resolve the city from list and pass it as an property to your person-component.
Also make sure to consider what is the purpose of the sub-object. Do you need it to be reactive, or is it static? In order to save bunch of memory you could just tell "person.city = city", which will be injected for each and every person-component, but if it needs to be reactive then you need to use Vue.set -method... and remember that if each city needs to be own instance (so that each person has similar city-object, but properties need to be editable per person) then you need to make sure that you are not using referred object! Thus you most likely need to clone the city-object, which will eat up browsers memory.
Your micro-component might contain separate view-states for both read-only-state and editor-state. This is quite common. Still, you are actually creating instance of that micro-component every time when, thus initializing that component thousands of times.
Think of situation where you have Excel-like spreadsheet with table and table-rows. Each cell contains your custom "my-input" -component, which takes "readonly"-property from your layout. If the UI is on the readonly-state then you are displaying only the label part inside that my-input-component, but otherwise you are displaying input-tag with some special conditions (such as having different input for datetime, number, text, textarea, select-tag etc). Now let's assume you have 100 rows with 20 columns, so you are actually initializing 2000 my-input-components. Now the question is -- what could be improved (performance-wise)?
Well, you could separate readonly-label from my-input-component to your list-view, so that you either display readonly-version (label) OR you you display the editable my-input-component. This way you have v-if condition, which makes sure that those 2000 micro-components won't be initialized unless you have specifically requested to initialize 'em (due either row or whole layout moving from readonly -> editable -state)... You probably guess how big the impact is memory-wise for browser, when Vue doesn't need to create 2000 components.
If you are facing that your page loads really slow it might not be VUE at all. Check out the amount of HTML-tags rendered to your HTML. HTML performs rather poorly when you have large amounts of tags. One of the simplest ways to demonstrate this is by repeating select-tag with 2000 options 100 times, or by having a single 20000 option select-tag. The same way you might be overflowing the amount of html-tags by having lots of micro-components with unnecessary wrapping divs etc... The less depth and less tags you have, the less rendering performance is required from browser & CPU.
Try to learn good HTML-tag architecture via examples. For an example you could study how Trello -services dashboard-view has been programmed. It's quite simple and beautiful representation of rather semi-complex service, with minimal amount of sub-divs.
There are many ways to improve memory handling, but I'd say that most important ones relate to separating "hidden" objects from visible objects, as described on my original answer. Second part is understanding the difference or instanced vs referenced objects. Third is to limit the amount of unnecessary data-passing between objects.
Personally I haven't tried this, but there exists a Vue-virtual-scroller component which handles any amount of data by simply being a wrapper for seemingly infinite amounts of data. Check out the concept # https://github.com/Akryum/vue-virtual-scroller , and let me know if it solved the problem for you.
I hope these guidelines give some ideas for optimizing your components. Never give up the hope, there is always room for improvement!
From everything I've read, I see that you just don't need reactivity for that data, because:
Each row within table is toggleable, meaning that clicking the row changes the row to edit-mode, which enables Excel-like editing for that specific field/cell
This means rows are not editable and data cannot be mutated without user interaction.
Each object has about ~100-150 fields/properties, but only certain amount of 'em are shown at any given moment within table (table columns can be toggled in real-time).
You keep fields reactive but not display them.
And now your questions
Is there a way to toggle reactivity for specific array-list objects (by index or such), so that objects within array itself are unobserved/non-mutable unless specifically called to become mutable (ie. when user clicks row, which enables edit-mode)?
If there's a single item that can be edited at a time, then why keep everything reactive? You can easily use a single variable to listen for that changes.
How would you implement handling of large datasets for Vue, as reactivity seems to bottleneck the memory usage?
It's all about implementation - you rarely end up in a situation when you need a huge list of items to be reactive. The more items you have, the more events needs to happen in order to use the reactivity. If you have 50k items and there are just a few events to mutate (like user modifying data manually), then you can easily listen for those events and make the reactivity manually rather than leave Vue handle all the data. You can check Vuex that can make your life a bit easier for you :)
One idea I have is to turn that "items" -dataset to non-observable/non-reactive with Object.freeze or some similar approach and have table to render two datasets: one for non-reactive and one for those which are currently within edit-mode (which would be pushed to "editableItems" dataset when row is clicked)
This is kind of going in the right direction but there is no need to support two arrays. Imagine using something like this:
data: function() {
return {
editingItem: {}
// when editing is enabled bind the input fields to this item
}
},
created: function() {
this.items = [] // your items, can be used in markdown in the loop, but won't be reactive!
},
watch: {
editingItem: function(data) {
// this method will be called whenever user edits the input fields
// here you can do whatever you want
// like get item's id, find it in the array and update it's properties
// something like manual reactivity ;)
}
}
I had this exact problem where I needed to display a huge list, think 50000 items at least of variable height and I could not find any solution for it
The general solution is to build/use a virtual scroll.
It only keeps a few items in DOM while the rest of them are edited in the DOM. It however keeps changing what is visible depending on whether you scroll up/down
The existing libraries I find do not deal with dynamic heights unless you HARDCODE the heights like vue-virtual-scroller and vue-virtual-scroll-list
vue-collection-cluster allows you to dynamically calculate heights but lags miserably at 50000 items
So I came up with my own solution that scrolls SUPER SMOOTH at 50000+ items, even tested with 100k items and works pretty well
The idea of the implementation for dynamic row heights goes like this
We need to maintain a list of heights for each item in an array
Based on where the scroll Top is we apply a transform translateY vertically to offset the few items that we show the user at all times
I have added ENOUGH comments in the solution for you to easily figure out what is going on
HTML
<script type="text/x-template" id="virtual-list">
<div id="root" ref="root">
<div id="viewport" ref="viewport" :style="viewportStyle">
<div id="spacer" ref="spacer" :style="spacerStyle">
<div v-for="i in visibleItems" :key="i.id" class="list-item" :ref="i.id" :data-index="i.index" #click="select(i.index)" :class="i.index === selectedIndex ? 'selected': ''">
<div>{{ i.index + ' ' + i.value }}</div>
</div>
</div>
</div>
</div>
</script>
<div id="app">
<h1 class="title">
Vue.js Virtual + Infinite Scroll + Dynamic Row Heights + Arrow Key Navigation + No Libraries
</h1>
<p class="subtitle">
No hardcoding of heights necessary for each row. Set emitEnabled to false
for max performance. Tested with <span id="large_num">50000</span> items...
</p>
<div id="list_detail">
<div id="list">
<virtual-list></virtual-list>
</div>
<div id="detail">
<table>
<tbody>
<tr>
<th class="caption">Root Container Height</th>
<td>{{store['root-height']}} px</td>
</tr>
<tr>
<th class="caption">Viewport Height</th>
<td>{{store['viewport-height']}} px</td>
</tr>
<tr>
<th class="caption">Smallest Row Height</th>
<td>{{store['smallest-height']}} px</td>
</tr>
<tr>
<th class="caption">Largest Row Height</th>
<td>{{store['largest-height']}} px</td>
</tr>
<tr>
<th class="caption">Scroll Top</th>
<td>{{store['scroll-top']}} px</td>
</tr>
<tr>
<th class="caption">Page Index</th>
<td>{{store['page-start-index']}}</td>
</tr>
<tr>
<th class="caption">Start Index</th>
<td>{{store['start-index']}}</td>
</tr>
<tr>
<th class="caption">End Index</th>
<td>{{store['end-index']}}</td>
</tr>
<tr>
<th class="caption">Translate Y</th>
<td>{{store['translate-y']}} px</td>
</tr>
</tbody>
</table>
<p><b>Visible Item Indices on DOM</b> {{store['visible-items']}}</p>
<p><b>Total Height Till Current Page</b> {{store['page-positions']}}</p>
<p>
<b>Row's Vertical Displacement From Viewport Top</b>
{{store['row-positions']}}
</p>
<p><b>Heights</b> {{store['heights']}}</p>
</div>
</div>
</div>
CSS
#import url('https://fonts.googleapis.com/css?family=Open+Sans&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/**
Apply Scroll Bar Styles
https://css-tricks.com/the-current-state-of-styling-scrollbars/
*/
html {
--scrollbarBG: #181C25;
--thumbBG: orange;
}
body::-webkit-scrollbar {
width: 11px;
}
body {
scrollbar-width: thin;
scrollbar-color: var(--thumbBG) var(--scrollbarBG);
}
body::-webkit-scrollbar-track {
background: var(--scrollbarBG);
}
body::-webkit-scrollbar-thumb {
background-color: var(--thumbBG) ;
border-radius: 6px;
border: 3px solid var(--scrollbarBG);
}
html {
height: 100%;
}
body {
min-height: 100%;
height: 100%;
padding: 2rem;
color: #AAA;
background: #181C25;
font-family: 'Open Sans', sans-serif;
font-size: 0.9rem;
line-height: 1.75;
}
#app {
height: 100%;
display: flex;
flex-direction: column;
}
#list_detail {
display: flex;
height: 70%;
}
#list {
flex: 2;
height: 100%;
}
#detail {
flex: 1;
padding: 1rem;
overflow: auto;
height: 100%;
}
#root {
height: 100%;
overflow: auto;
}
.list-item {
padding: 0.75rem 0.25rem;
border-bottom: 1px solid rgba(255, 255, 0, 0.4);
}
.title {
color: white;
text-align: center;
}
.subtitle {
color: orange;
text-align: center;
}
table {
width: 100%;
table-layout: fixed;
text-align: center;
}
th.caption {
text-align: left;
color: #00BEF4;
font-weight: 100;
padding: 0.5rem 0;
}
td {
text-align: left;
}
b{
font-weight: 100;
color: #00BEF4;
}
#large_num {
color: red;
}
.selected {
background: midnightblue;
}
Vue.js
I am getting limited to 30000 characters here on SO and therefore HERE is the complete code on CodePen
Limitations
Does not play nice with screen resize at the moment, working on it
Features
50000+ items effortless scroll
Arrow navigation supported just like a native list
If you have any questions, let me know in the comments
To render large list of components I just found https://github.com/RadKod/v-lazy-component
really love it. It just use the Intersection API to render, or not, the component. It removes the non-visible lazy loaded components and load the visible ones in a very smooth way with no innecesary complications.

Is there any way to change the text size (font size) of specific blocks when you using asciidoc?

I need your help.
Now I am using AsciiDoc and AsciiDoctor to create some manuals.
I want texts smaller on some specific blocks, for example wide table, wide list, and so on, but not want main texts smaller.
Especially I need to make texts of wide tables smaller as my customer requests so.
Is there any way?
You mention lists and tables...
About lists, it can't be done as stated in AsciiDoctor Documentation:
Unsupported
Complex AsciiDoc markup is not permitted in attribute values, such as:
lists
multiple paragraphs
other whitespace-dependent markup types
As you can see, there it mentions multiple paragraphs, so while #EhmKah answer is a correct way to set a custom styling block, it won't be rendered as expected in a table/list as it's multi-paragraph.
The Built-in CSS class syntax is the way to go [small]#any phrases#
But in order to make this work in a table, you must set the cell type with a specifier in this case, the AsciiDoc specifier denoted by a
This means the cell (or column) will render supported AsciiDoc statements, attributes, etc.
Here's a working example:
[frame="none",grid="none"]
|====
a| image::images\logo.png[] a|[.small]#Autor: {author}#
|====
If you have tons of rows/columns, you don't have to manually apply the a to all of them. You can set the columns you need this behavior this way:
[cols="1a,2a",frame="none",grid="none"]
|====
| image::images\logo.png[] |[.small]#Autor: {author}#
|====
You can check its documentation for more about Column Formatting and you can check the Rendered table with variable widths and alignments sub section for more about AsciiDoc (a) and other specifiers.
docinfo.html + --attribute docinfo=shared
You can drop your CSS modifications into a file called docinfo.html:
<style>
/* Your custom CSS. */
</style>
and then build with:
asciidoctor --attribute docinfo=shared README.adoc
and that makes Asciidoctor 2.0.10 place docinfo.html at the bottom of the <head> element.
So you can override most of the default Asciidoctor style from there.
Then it's just a matter of understanding the generated HTML and previous style definitions to override them.
For image specifically, see also: How to set a custom image height for an image in Asciidoctor?
When you use a theme file, you can add a role to it like this:
role:
mycustomfont:
font-color: #333
font-size: 10
Now you can reference your newly created role right from your table cell:
a|[.mycustomfont]# some text #
I read something about
[small] and [%autofit] option https://github.com/asciidoctor/asciidoctor-pdf/issues/185 I never needed it so maybe you give it a try.
example-code
[small]
----
should be rendered in smaller font.
----
[%autofit]
----
really long text that doesn't want to fit on a single line with the default font size, so we'll make it shrink to fit.
----

Stacking CSS3 Structural pseudo-classes

While practicing different scenarios in which CSS3 pseudo-classes and selectors might come in handy, I ran across something I just can't figure out!
Here's the situation. I want to modify the :first-of-type::first-letter of the first non-empty paragraph for a block of text. I'm not sure that pseudo-classes can be stacked though. Here's what I've come up with (Doesn't work, of course)
.article-body > p:not(:empty):first-of-type::first-letter { ... }
Given the following markup:
<div class="article-body">
<p></p>
<p>The "T" in this paragraph should be the one affected.</p>
</div>
The only guess I can come up with is that pseudo-classes (ie; :not() and :first-of-type) can not be stacked upon each other. Which is odd since you can stack pseudo-elements upon each other, and other pseudo-classes...
Any ideas for how this can be accomplished?
:first-of-type selects the first p, as the name suggests, not the first non-empty p as you might want.
They stack just fine, but :first-of-type purely operates on the tag (i.e. type), not on the preceding complex selector. So you just end up looking for the first p, and that first paragraph also shouldn't be empty. And that doesn't exist.
Assuming empty paragraphs might appear throughout the text, and you only want the first, non-empty paragraph to be affected, I don't think it's possible to do this with just one selector. This is the best I can come up with:
p:first-of-type::first-letter,
p:empty + p::first-letter { text-transform: uppercase; /* ... */ }
p:not(:empty) ~ p::first-letter { text-transform: inherit; /* reset */ }
That will apply the CSS only to the first non-empty paragraph (well, and to a first empty paragraph, but it won't do anything then anyway).
Removing the empty paragraph causes
.article-body > p:first-of-type::first-letter { ... }
to behave properly. Is there any reason that the empty paragraph needs to be there? Can you alter the spacing of the first paragraph to account for the empty one not being there?

Resources