Maintain Aspect Ratio Mixin on Open Event Frontend

The welcome page of the Open-Event-Frontend is designed to contain cards that represent an event. A user is directed to the event-details page by clicking on the corresponding card. The page consists of an image that serves as the banner for the event and an overlapping div to provide some contrast against the image. A comment may also be added onto the image and along with the overlapping div it is wrapped in a container div.

Since we have given a specific height to the contrasting div, the background image shrinks according to the screen size but the contrasting div does not whenever we go from a large screen to a smaller screen.

Mobile view (before):-

We want our contrasting div also to resize in accordance to the image. To do it, we first define a sass mixin to maintain a common aspect ratio for image and overlapping div. Let us see it’s code.

 @mixin aspect-ratio($width, $height) {
    position: relative;
    &:before {
      display: block;
      content: "";
      width: 100%;
      padding-top: ($height / $width) * 100%;
    }
    > .content {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
    }
    > img {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
    }
  }

So what does this mixin actually doing is we are passing the height and width of the image and we are defining a pseudo element for our image and give it a margin top of (height/width)*100 since this value is related to image width (height: 0; padding-bottom: 100%; would also work, but then we have to adjust the padding-bottom value every time we change the width). Now we just position the content element and image as absolute with all four orientations set to 0. This just covers the parent element completely, no matter which size it has.

Now we can simply use our mixin by adding the following line of code in out container div

@include aspect-ratio(2, 1);

Here we want to maintain a 2:1 aspect ratio and the user is also expected to upload the image in the same aspect ratio. Therefore, we pass width as 2 and height as 1 to our mixin.

Now when we resize our screen both the image and the overlapping div resize maintaining 2:1 aspect ratio.

Mobile view (after):-

Resources

  • mademyday blog describes a method of using pseudo elements to maintain an element’s aspect ratio.
  • css-tricks snippet.

How Device Service Makes it Easy to Implement Responsive UI in Open Event Frontend

This blog article will illustrate how the device service which has been used frequently in Open Event Frontend, works to make the UI responsive and render it selectively based on device side. To quote the official documentation,

An Ember.Service is a long-lived Ember object that can be made available in different parts of your application.

The device service is precisely that. It is available universally across the app  and its chief purpose is to provide an object through out the app, which allows us to actively determine the device size at the instant of rendering.  This allows us to have a very easy implementation of a highly responsive UI and also makes it redundant to use  css media queries to achieve similar results. The services allow us to maintain a persistent connection and has to be injected. Like all other ember entities, the boiler plate code of a service may be generated via simply using Ember CLI.

$ ember generate service device

To begin with we define the various breakpoints (in terms of width of the screen) that we want in our app. We will be keeping this outside the service object to keep it lean and faster to loop over. We need to ensure that these break points are exactly the same ones used for semantic UI. This is because we want to be aware of what device the current width represents according to semantic UI because various components of semantic UI behave according to these device sizes. For instance various fields of a form may be stackable only for mobiles, and not for tablets.

const breakpoints = {
mobile: {
  max : 767,
  min : 0
},
tablet: {
  max : 991,
  min : 768
},
computer: {
  max : 1199,
  min : 992
},
largeMonitor: {
  max : 1919,
  min : 1200
},
widescreen: {
  min: 1920
}

Our goal is to iterate over these breakpoints and compare the window width with them, and then assign the device type to the required variable. So we begin with the iterating loop, Also we will always need to keep track of the current screen width, hence we define currentWidth property, whereas the deviceType property will keep track of the current device using currentWidth and the breakpoints. These all will be defined inside the service object. The logic for deviceType property is basically to iterate over all the breakpoints and then it checks if the current width of the document lies within the range of a particular breakpoint.

export default Service.extend({

currentWidth: document.body.clientWidth,

deviceType: computed('currentWidth', function() {
  let deviceType = 'computer';
  const currentWidth = this.get('currentWidth');
  forOwn(breakpoints, (value, key) => {
    if (currentWidth >= value.min && (!value.hasOwnProperty('max') || currentWidth <= value.max)) {
      deviceType = key;
    }
  });
  return deviceType;
}),
})

 

Now it is possible for us to use the deviceType property to calculate other useful properties for device type. For instance, we can add the following to the service object.The very point of using this service is the fact that, though semantic UI supports these breakpoints for devices of various sizes but it doesn’t allow us to use them as boolean properties on the basis of which we can decide, which content to render and which not. Also, since the breakpoints are exclusive, there is no possible overlapping of the properties by them being true simultaneously.Using equal operator is a safe way to compare a computed property.

isMobile       : equal('deviceType', 'mobile'),
isComputer     : equal('deviceType', 'computer'),
isTablet       : equal('deviceType', 'tablet'),
isLargeMonitor : equal('deviceType', 'largeMonitor'),
isWideScreen   : equal('deviceType', 'widescreen'),

One very important thing we should realise is, that even the the deviceType is observing currentWidth, however document.body.clientWidth is not binded, and thus currentWidth needs to be calculated, every time the window is resized, so we add an init() for the service. It will make sure that whenever the window is resized, the currentWidth object will be initialised.

init() {
this._super(...arguments);
$(window).resize(() => {
  debounce(this, () => {
    this.set('currentWidth', document.body.clientWidth);
  }, 200);
});}

This completes the service, now we can see from the following example how will this service be used. In this example we try to make the menu responsive, by using an icon only menu for mobile devices where as a full menu for larger ones. The properties of the service may simply be used via device.<property>.

{{#if device.isMobile}}
 <.div class= ui grouped  icon buttons>
   <.div class=”ui button><.i class=”checkmark icon><./div>
   <.div class=”ui button><.i class=”cancel icon><./div>
 <./div>
{{else}}
 <.div class= ui grouped buttons>
   <.div class=”ui button>Apply<./div>
   <.div class=”ui button>Cancel<./div>
 <./div>
{{/if}}

Resources

**image is licensed under free to use CC0 Public Domain

Responsive Image Overlay

Image overlay is a very common concept in front-end development. It is easy to implement but difficult when we deal it with different screen sizes, where we need to cover the image with the overlay each time the screen size is changed. I have gone through various blog posts when I need to implement the same for Open-event webapp and researched a solution that works for all screen sizes without any media query.

1234

How to add an overlay to an image ?

If we need four images in a single row nearly 300*300px.  The code below shows the markup.

image-holder : The parent class to take the image and overlay inside it.

background-image: This class takes image source.

responsive-overlay: This is the key point to make it responsive. Responsive-overlay contains a class hover-state to add overlay absolutely and a class social-links.

social-links: It adds content to hover-state.

 

<div class="image-holder">
  <img class="background-image" alt="" src="">
   <div class="responsive-overlay">
     <div class="hover-state text-center preserve3d">
       <div class="social-links vertical-align">

       </div>
     </div>
   </div>
 </div>

The styling is written with SASS in .scss file as shown below.

//overlayimage and backgroundshade can be set in config.scss

 .image-holder {
   position: relative;
   overflow: hidden;
   margin-bottom: 12px;

   .background-image {
     height: 300px;
     width: 300px;
     display: block;
     margin: 0 auto;
     background-color: $background-shade;
    }
 
   .responsive-overlay {
     @include responsiveoverlay;

    .preserve3d {
       height: 300px;
      }

    .hover-state {
     @include hoverstate;
     height: 300px;
     width: 300px;
    }

  @mixin responsiveoverlay {
     height: 100%;
     position: absolute;
     top: 0;
     width: 100%;
}

   @mixin hoverstate {
     background: $overlayimage;
     display: block;
     height: 300px;
     left: 0;
     margin: 0 auto;
     opacity: 0;
     position: relative;
     top: 0;
     -moz-transition: all 0.3s ease-out;
     -webkit-transition: all 0.3s ease-out;
     transition: all 0.3s ease-out;
     width: 300px;
     z-index: 2;
   }

This code will work for responsiveness as well. The main catch here is the responsive-overlay class which is made 100% in width but set to position absolute. The images which are 300 * 300 px in size will take an overlay of the same size because of hover-state class. Instead, if we adjust sizes of images in small screens the above code will adjust overlay on the image automatically.

Like, on tablets we can have an overlay like this.

345

And on mobile screen output is like that :

23

Conclusion

Responsiveness is easy if we follow correct concepts. Here, the concepts of absolute and relative positioning in CSS have done the magic. Now we can play by adding different contents and effect on hover following the same basics.

Responsive UI: Testing & Reporting

Few days back I wrote a blog about how to make a website responsive. But how do you exactly check for responsiveness of a website? Well, you can do it using your browser itself. Both Mozilla Firefox and Google Chrome provides responsive mode for testing responsiveness of websites for standard resolutions and also to drag and check for any resolution. Open Event Organizer’s Server has quite some responsive issues, so I would tell about reporting the issues as well.

Continue reading Responsive UI: Testing & Reporting

Responsive UI: Modifying Designs with Device Width

An important feature of websites these days with the advancement of smartphones is being responsive with device size. We nowadays not only worry about the various width of laptop or desktop, which don’t vary by a huge amount but also need to worry about tablets and phones which have a much lesser width. The website’s UI should not break and should be as easy to use on phones as it is on desktops/laptops. Using frameworks like bootstraps, Semantic-UI solves this problem to a large extent. But what if we need to modify certain parts by our own in case of mobile devices? How do we do that?

Continue reading Responsive UI: Modifying Designs with Device Width

Working with Absolute Positioning

During the past week, I have done a lot of work for making the feature that allow the users to view the schedule of events according to track and time. The toughest part was to have a headstart to think of the mockup that fulfills this criterion.

After the mockup, I started coding it and realized that I have to add CSS by using Javascript. The frontend needs to calculate the top of each pop-up that appears when the track is hovered and to append the box just below it.

a

The interesting part was to calculate the top each time the element is hovered and to place the box at the right position by using jQuery.

Position Absolute 

The difficulty becomes maximum when we use “position : absolute “. As, it takes the element out of the layer. The element with absolute positioning is difficult to handle when it comes to responsiveness. Here also the pop-overs showing the speakers are to be made with absolute positioning.

The code snippet shows the calculation for exact position of the pop-up box.

$(document).ready(function(){

 $('.pop-box').hide();
 $('.item').hover(function (event) {

 event.preventDefault();
 event.stopPropagation();
 var track = $(event.target);
 var link = track.children(0);
 var offset =$(link).offset();

 var position= offset.top-link.height()-30;
 if( $(window).width()<600){
 var position= offset.top-link.height()-48; 
 }
 if(offset.top){
 
 $('.pop-box').hide();
 var p=$(this);
 $(p).next().show();
 var posY = event.pageY;
 nextOfpop=$(p).next();
 
 var toptrack = position ;

 $(nextOfpop).css({'top':toptrack
                 });

 $(document).mouseup(function (e)
 {
 var container = $(".pop-box");

  if (!container.is(e.target) 
  && container.has(e.target).length === 0 && (e.target)!=$('html').get(0)) 
   {
   container.hide();
   }
   });
  });
 })

This code sets the value of top of the pop-over in the variable position and copy it to toptrack that is passed to CSS to adjust the top dynamically.

Responsiveness for top

I was struggling a whole day to find out the best possible way for the responsiveness of track page. Obviously, the difficult part was the recalculation of top with the screen-size. Currently I have used $window.width() to check the width of screen and adjust the top on mobile. But, it will include more complexity when it is done for other screen sizes rather than mobile.

 if( $(window).width()<600){
 var position= offset.top-link.height()-48; 
 }

The tracks page is ready now with both light and dark theme.

10.png

That’s how the position absolute is handled with jQuery. To remove the complexity, all the CSS except the calculation of top is written with SASS.