'inherit','size'=>0,'sizeTablet'=>0,'sizeMobile'=>0,'weight'=>0,'lineHeight'=>0,'letterSpacing'=>0,'color'=>'','hoverColor'=>''];}return ['enabled'=>false,'styles'=>$styles];} public static function clean($raw): array { if(!is_array($raw)||!is_array($raw['styles']??null)){throw new \InvalidArgumentException('Invalid global typography settings.');}$out=self::defaults();$out['enabled']=!empty($raw['enabled']); foreach(self::ROLES as $role){$v=$raw['styles'][$role]??[];if(!is_array($v)){throw new \InvalidArgumentException('Invalid typography role.');}$s=&$out['styles'][$role]; $s['family']=FontLibrary::clean($v['family']??'inherit'); foreach(['size','sizeTablet','sizeMobile'] as $key){$s[$key]=Document::number($v[$key]??0,0,120);} $s['weight']=(int)Document::number($v['weight']??0,0,900);$s['lineHeight']=Document::number($v['lineHeight']??0,0,3);$s['letterSpacing']=Document::number($v['letterSpacing']??0,-3,12); $s['color']=Document::color($v['color']??'');$s['hoverColor']=Document::color($v['hoverColor']??'');unset($s); }return $out; } public function routes(): void { $manage=static fn()=>current_user_can('manage_options'); register_rest_route('digiter-builder/v1','/typography',[ ['methods'=>'GET','permission_callback'=>$manage,'callback'=>static function(){return self::response(['revision'=>ThemeBuilder::options()['revision'],'typography'=>ThemeBuilder::options()['typography']]);}], ['methods'=>'PUT','permission_callback'=>$manage,'callback'=>static function($r){ try{$styles=self::clean($r->get_param('typography'));}catch(\InvalidArgumentException $e){return new \WP_Error('digibl_typography',$e->getMessage(),['status'=>400]);} $next=ThemeBuilder::options();$next['typography']=$styles;$saved=ThemeBuilder::commit($next,(string)$r->get_param('revision')); return is_wp_error($saved)?$saved:self::response(['revision'=>$saved['revision'],'typography'=>$saved['typography']]); }], ]); register_rest_route('digiter-builder/v1','/typography/preview',['methods'=>'POST','permission_callback'=>$manage,'callback'=>static function($r){ try{$styles=self::clean($r->get_param('typography'));}catch(\InvalidArgumentException $e){return new \WP_Error('digibl_typography',$e->getMessage(),['status'=>400]);} $doc=Document::blank();$doc['nodes']=[];foreach(range(1,6) as $level){$doc['nodes'][]=['id'=>'type'.$level,'type'=>'heading','props'=>['text'=>'Heading '.$level,'level'=>'h'.$level],'children'=>[]];} $doc['nodes'][]=['id'=>'body','type'=>'text','props'=>['content'=>'

Readable text brings a layout together. Example link.

'],'children'=>[]];$doc['nodes'][]=['id'=>'button','type'=>'button','props'=>['label'=>'Example button','url'=>'https://example.com'],'children'=>[]]; $doc=Document::clean($doc);$previous=self::$preview;self::$preview=$styles;try{$html=(new Renderer(true))->render($doc);}finally{self::$preview=$previous;}return self::response(['html'=>$html]); }]); } private static function response($data){$r=new \WP_REST_Response($data);$r->header('Cache-Control','no-store, private');return $r;} public static function current(): array {return self::$preview??ThemeBuilder::options()['typography'];} public static function styles(array $settings): array { $global=self::current();if(empty($global['enabled'])){return ['css'=>'','class'=>''];} $name='dbs-type-'.substr(hash('sha256',wp_json_encode([$global,$settings['bodyFont']??0,$settings['titleFont']??0,$settings['bodySize']??0,$settings['lineHeight']??0,$settings['mobileBreakpoint'],$settings['tabletBreakpoint']])),0,12);$scope='.dbs-document.'.$name;$css=''; foreach($global['styles'] as $role=>$s){ $selector=$role==='body'?$scope:($role==='link'?$scope.' a:not(.dbs-button)':($role==='button'?$scope.' .dbs-button':$scope.' '.$role));$declarations=''; if($s['family']!=='inherit'){ $family=FontLibrary::stack($s['family']);if(str_starts_with($role,'h')){$family='var(--dbs-heading-font,'.$family.')';} if($role!=='body'||empty($settings['bodyFont'])){$declarations.='font-family:'.$family.';';} } if($s['weight']){$declarations.='font-weight:'.$s['weight'].';';} if($s['lineHeight']&&($role!=='body'||empty($settings['lineHeight']))){$declarations.='line-height:'.$s['lineHeight'].';';} if($s['letterSpacing']){$declarations.='letter-spacing:'.$s['letterSpacing'].'px;';} if($s['color']){$declarations.='color:var('.($role==='button'?'--dbs-button-text':'--dbs-local-ink').','.$s['color'].');';} if($declarations){$css.=$selector.'{'.$declarations.'}';} if($s['hoverColor']&&in_array($role,['link','button'],true)){$css.=$selector.':hover,'.$selector.':focus-visible{color:'.$s['hoverColor'].';}';} if($role==='body'&&!empty($settings['bodySize'])){continue;} foreach(['size','sizeTablet','sizeMobile'] as $i=>$key){if(!$s[$key]){continue;}$rule=$selector.'{font-size:'.$s[$key].'px;}';$css.=$i?'@media(max-width:'.(int)$settings[$i===1?'tabletBreakpoint':'mobileBreakpoint'].'px){'.$rule.'}':$rule;} } // Local module sizes keep precedence and inherit down to smaller viewports. foreach(['desktop','tablet','mobile'] as $i=>$device){$base=$scope.' .dbs-font-'.$device.'-custom';$selectors=[$base.'>.dbs-heading',$base.'>.dbs-prose',$base.'>.dbs-bubble',$base.'>.dbs-hero .dbs-headline'];$rule=implode(',',$selectors).'{font-size:var(--dbs-font-'.$device.');}';$css.=$i?'@media(max-width:'.(int)$settings[$i===1?'tabletBreakpoint':'mobileBreakpoint'].'px){'.$rule.'}':$rule;} return ['css'=>Assets::inline_style($css,'typography'),'class'=>' '.$name]; } }