7

Writing an extension for a Plugin I have the possibility to change all attributes of an HTML element using PHP.

$attributes["style"] .= 'padding-left:10px;';
array_push($attributes["class"], "long-container");
array_push($attributes["class"], "super smooth");
$attributes["data-whatever"] = "great";

Now I want to give a user the possibility to enter the width / height ratio of a div dynamically (the solution of how to do this is described in the answer by @Web_Designer here: Maintain the aspect ratio of a div with CSS).

Within the function where I can change the output of the third-party Plugin I wrote the following code for calculating the width height ratio according to the input. As the height of my boxes is :

if( !empty( $args['stretchy-desktop'] ) ) {
    $sd = array_map('trim',explode(":",$args['stretchy-desktop']));

    if(count($sd)==2) {
        $sd[0] = floatval(str_replace(",",".",$sd[0]));
        $sd[1] = floatval(str_replace(",",".",$sd[1]));

        if($sd[0]>0 && $sd[1]>0) {
            $padding = ($sd[1] / $sd[0])*100;
            array_push($attributes['class'], 'stretchy-desktop');
            $attributes['style'] .= 'padding-bottom:'.$padding.'%;';
        }
    }
}

Great right? However now the user wants a possibility to enter a different weight height ratio for mobile devices as well as a different dynamic min-height for mobile devices and this is there I am stuck.

1) It is not possible to give inline @media queries right now otherwise my solution would be like this (Is it possible to put CSS @media rules inline?):

$attributes['style'] .= '@media (min-width:540px) {padding-bottom:'.$padding.'%;}@media (max-width:539px) {padding-bottom:'.$padding_mobile.';}';

2) It is not possible to use HTML attribute values in CSS right now (CSS values using HTML5 data attribute) otherwise my solution would be like this:

$attributes['data-desktoppadding'] = $padding;
$attributes['data-mobilepadding'] = $padding_mobile;

In CSS:

@media (min-width:540px) {
.long-container {
padding-bottom: attr(data-desktoppadding);
}
}

@media (max-width:539px) {
.long-container {
padding-bottom: attr(data-mobilepadding);
}
}

3) As the values are dynamic numbers I can not define a CSS class for every possible existing float.

Of course I could use JavaScript but we all know the significant drawbacks (including ugly page load).

Can you think of any CSS solution for this dilemma?

Community
  • 1
  • 1
Blackbam
  • 17,496
  • 26
  • 97
  • 150
  • Are you able to create, or write to, a ` – Just a student Mar 27 '17 at 12:55
  • After thinking about the problem a lot I came to this idea. It is kind of a solution but not a good one because my only possibility would be to save all styles to a kind of global variable and generate dynamic styles in the footer of the page. Still I hope somebody comes up with whatever better idea. – Blackbam Mar 27 '17 at 13:41
  • I have a solution but it involves creating a extra wrapper div around the target element. Are you able to do that? – Cave Johnson Mar 27 '17 at 16:16
  • @KodosJohnson Not easily but so far it sounds like the best suggestion. Can you share your solution as an answer? – Blackbam Mar 27 '17 at 16:32
  • 2
    _"because my only possibility would be to save all styles to a kind of global variable and generate dynamic styles in the footer of the page"_ - of course that would not be the only possibility. A proper logic of input/processing/output would solve that, in a pinch output buffering could do it as well. And making it an external resource that could simple be embedded with a `link` element would also be a possibility (can easily be called with parameters, to output styles specific to a page or a certain piece of content.) – CBroe Mar 27 '17 at 18:49
  • I only want to mention that i like that extension in a plugin in a module in an addon pattern. – user2659982 Mar 31 '17 at 17:33
  • @CBroe Basically this is true. However in my concrete case I use the WordPress page builder and this comes from a custom widget solving this with output buffering or pre-processing would be quite resource consuming and clumsy. – Blackbam Mar 31 '17 at 19:19

2 Answers2

6

Here is a solution I came up with. It involves creating a wrapper div around the target element. Basically, the way that this works is that the outer div is assigned the inline styles for the mobile mode, and the inner div is assigned the inline styles for desktop mode. When the browser window is resized to be below the threshold for desktop view, it resets the inner div's (desktop) inline styles to defaults so the outer div's (mobile) inline styles take over. When the browser window is resized to be above the threshold, it resets the outer div's (mobile) inline styles to defaults so the inner div's (desktop) inline styles take over. The way that it overrides the inline styles is by using the !important keyword in the rulesets in the CSS media queries.

I think it goes without saying that the inline styles in the snippet below would be replaced with your $attributes['style']. But since you will have separate mobile and desktop styles, I guess it would be $attributes['style-mobile'] and $attributes['style-desktop'].

@media (min-width:540px) {
    .padding-mobile {
        padding-bottom:0 !important;
        width: auto !important;
    }
}
   
@media (max-width:539px) {
    .padding-desktop {
        padding-bottom:0 !important;
        width: auto !important;
    }
}
<div class="padding-mobile" style="width:100%;background-color:red;padding-bottom:100%;">
    <div class="padding-desktop" style="width:50%;background-color:red;padding-bottom:25%;">
    
    
    </div>
</div>
Cave Johnson
  • 6,499
  • 5
  • 38
  • 57
5

An elegant approach that works in most major browsers is the usage of custom properties. They are basically variables in CSS. As of writing this (2017-03-27), only IE and Edge do not support this, although they are working on support for Edge.

You would add the variables to the $attributes['style'] and actually apply them in the stylesheet inside a media query. They are then used dynamically by the browser.

I have implemented a demo as a snippet, but because it is easier to change the viewport size on JSFiddle, also a copy of the demo there. Note that the responsive breakpoint is defined in CSS here, and the variables are defined in inline styles of the element.

.container {
  width: 200px;
}

.block {
  position: relative;
  display: block;
  box-sizing: border-box;
  width: 100%;
  height: 0;
  padding: 10px;
  padding-bottom: var(--desktop-ratio);
  background: #bada55;
  color: #444;
}

@media (min-width: 540px) {
  .mobile {
    display: none;
  }
}

@media (max-width: 539px) {
  .desktop {
    display: none;
  }
  
  .block {
    padding-bottom: var(--mobile-ratio);
  }
}
<div class="container">
  <div class="block" style="--desktop-ratio: 56.25%; --mobile-ratio: 200%;";>
  My aspect ratio is set via custom properties. It is <span class="mobile">1:2</span><span class="desktop">16:9</span>.
  </div>
</div>

It is (at least at the moment) apparently not possible to set a breakpoint using a variable. That is,

@media (min-width: var(--breakpoint)) { ... }

is apparently not understood by at least Firefox and Chrome at the moment.

 


 

Alternatively, building on Kodos Johnson's answer: if you have only a single breakpoint, you can make use of the padding-bottom and padding-top. One of the two defines the aspect ratio on small screens, and the other defines it on big screens. This removes the need to add a wrapper element. Here is an example, based on the one from Kodos' answer.

@media (min-width:540px) {
    .block {
        padding-top: 0 !important;
    }
}
   
@media (max-width:539px) {
    .block {
        padding-bottom: 0 !important;
    }
}
<div class="block" style="width: 50%;
                          padding-bottom: 25%;
                          padding-top: 100%;
                          background-color: red;">
</div>
Community
  • 1
  • 1
Just a student
  • 10,560
  • 2
  • 41
  • 69
  • Regarding custom properties: It is definitly a nice approach if it would be working in Internet Explorer. Not testet yet but this may work to a point: https://codepen.io/aaronbarker/pen/MeaRmL ? The second answer I can try tomorrow in order to see if it works. – Blackbam Mar 28 '17 at 11:52
  • Have you been able to try it out, @Blackbam? I unfortunately (fortunately?) do not have access to Internet Explorer, so I cannot test that polyfill, but it looks good. – Just a student Mar 31 '17 at 18:29
  • Fantastic answer. THANK YOU! This worked perfectly for building my Angular component that allows dynamic CSS flex gap amount for different screen sizes. – g0rb Jun 06 '23 at 01:54