Changeset 334420
- Timestamp:
- 06/16/2026 09:12:49 PM (5 months ago)
- Location:
- havenlytics-realty/1.15.5
- Files:
-
- 19 added
- 5 deleted
- 27 edited
- 1 copied
-
. (copied) (copied from havenlytics-realty/1.15.0 )
-
assets/css/base (deleted)
-
assets/css/components (deleted)
-
assets/css/havenlytics-compat.css (modified) (4 diffs)
-
assets/css/home/base.css (added)
-
assets/css/home/carousel.css (added)
-
assets/css/home/hero.css (added)
-
assets/css/home/sections.css (added)
-
assets/css/layouts (deleted)
-
assets/css/pages (deleted)
-
assets/css/polish.css (modified) (13 diffs)
-
assets/css/theme.css (modified) (1 diff)
-
assets/css/utilities (deleted)
-
assets/js/customizer.js (modified) (7 diffs)
-
assets/js/havenlytics-home.js (modified) (2 diffs)
-
assets/js/theme.js (modified) (6 diffs)
-
bin (added)
-
bin/build-release.ps1 (added)
-
bin/verify-release-manifest.php (added)
-
front-page.php (added)
-
functions.php (modified) (7 diffs)
-
inc/admin/realty-admin.php (modified) (2 diffs)
-
inc/custom-header.php (modified) (2 diffs)
-
inc/customizer.php (modified) (4 diffs)
-
inc/customizer/controls.php (modified) (1 diff)
-
inc/customizer/helpers.php (modified) (1 diff)
-
inc/customizer/homepage-css-output.php (modified) (2 diffs)
-
inc/customizer/loader.php (modified) (1 diff)
-
inc/customizer/sections-homepage.php (modified) (1 diff)
-
inc/customizer/selective-refresh.php (modified) (5 diffs)
-
inc/integration-fallbacks.php (added)
-
inc/integrations/havenlytics/body-classes.php (added)
-
inc/integrations/havenlytics/bootstrap.php (added)
-
inc/integrations/havenlytics/breadcrumbs.php (added)
-
inc/integrations/havenlytics/carousel.php (added)
-
inc/integrations/havenlytics/helpers.php (added)
-
inc/integrations/havenlytics/hero-map.php (added)
-
inc/integrations/havenlytics/homepage-assets.php (added)
-
inc/integrations/havenlytics/homepage-settings.php (added)
-
inc/integrations/havenlytics/plugin-shell.php (added)
-
inc/layout.php (modified) (1 diff)
-
inc/plugin-hooks.php (modified) (6 diffs)
-
inc/template-functions.php (modified) (1 diff)
-
inc/theme-loader.php (added)
-
readme.txt (modified) (14 diffs)
-
screenshot.png (modified) ( previous)
-
style.css (modified) (3 diffs)
-
template-parts/header/mobile-menu.php (modified) (1 diff)
-
template-parts/header/navigation.php (modified) (2 diffs)
-
template-parts/header/property-search-panel.php (modified) (1 diff)
-
template-parts/home/department-tabs.php (modified) (2 diffs)
-
templates/template-realty-home.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
havenlytics-realty/1.15.5/assets/css/havenlytics-compat.css
r334339 r334420 97 97 98 98 } 99 body.wp-theme-havenlytics-realty 100 .hvnly-property-single__carousel-slide 101 .hvnly-property-single__similar-content 102 .hvnly-property-single__btn { 103 104 color: var(--hvnly-color-white)!important; 105 106 } 107 108 99 109 /* ------------------------------------------------------------------------- 100 110 Property archive & grid … … 220 230 } 221 231 232 /* Homepage hero map — Customizer vh height (overrides plugin 550px fixed heights). */ 233 .hvn-realty-section--hero .hvn-realty-hero__map, 234 .hvn-realty-section--hero .hvn-realty-home-map, 235 .hvn-realty-section--hero .hvn-realty-home-map__canvas, 236 .hvn-realty-section--hero #hvnly-map-placeholder, 237 .hvn-realty-section--hero .hvnly-map-placeholder, 238 .hvn-realty-section--hero .hvnly-map-view, 239 .hvn-realty-section--hero .hvnly-map-container, 240 .hvn-realty-section--hero .hvnly-properties-map, 241 .hvn-realty-section--hero #hvnly-properties-map, 242 .hvn-realty-section--hero .leaflet-container { 243 height: var(--hvn-realty-hero-height, 70vh) !important; 244 min-height: var(--hvn-realty-hero-height, 70vh) !important; 245 max-height: none; 246 border-radius: 0; 247 } 248 249 @media (max-width: 640px) { 250 .hvn-realty-section--hero .hvn-realty-hero__map, 251 .hvn-realty-section--hero .hvn-realty-home-map, 252 .hvn-realty-section--hero .hvn-realty-home-map__canvas, 253 .hvn-realty-section--hero #hvnly-map-placeholder, 254 .hvn-realty-section--hero .hvnly-map-placeholder, 255 .hvn-realty-section--hero .hvnly-map-view, 256 .hvn-realty-section--hero .hvnly-map-container, 257 .hvn-realty-section--hero .hvnly-properties-map, 258 .hvn-realty-section--hero #hvnly-properties-map, 259 .hvn-realty-section--hero .leaflet-container { 260 height: var(--hvn-realty-hero-height-mobile, 50vh) !important; 261 min-height: var(--hvn-realty-hero-height-mobile, 50vh) !important; 262 } 263 } 264 222 265 .hvn-realty-home .hvnly-property-card, 223 266 .hvn-realty-home .hvnly-agent-card, … … 254 297 255 298 @media (max-width: 768px) { 299 body.wp-theme-havenlytics-realty 256 300 .hvn-realty-havenlytics-view .hvnly-container, 301 body.wp-theme-havenlytics-realty 257 302 .hvn-realty-havenlytics-view .hvnly-property-single__container, 303 body.wp-theme-havenlytics-realty 258 304 .hvn-realty-havenlytics-view .hvnly-agent-single__container { 259 305 padding-inline: var(--hvn-theme-space-sm, 0.75rem); … … 264 310 } 265 311 } 312 313 314 @media (max-width: 767px) { 315 body.wp-theme-havenlytics-realty 316 .hvnly-container{ 317 padding:0 ; 318 } 319 } 320 -
havenlytics-realty/1.15.5/assets/css/polish.css
r334339 r334420 63 63 flex-wrap: wrap; 64 64 align-items: center; 65 gap: 0.25rem 1.5rem;66 65 list-style: none; 67 66 margin: 0; … … 626 625 } 627 626 628 /* Blog pagination — see layouts.css (.hvn-theme-pagination) */629 630 627 /* ========================================================================== 631 628 PHASE 6 — Responsive layout grid … … 703 700 position: absolute; 704 701 inset: 0; 705 background: rgba(15, 23, 42, 0.55); 702 background: rgba(15, 23, 42, 0.58); 703 backdrop-filter: blur(4px); 704 -webkit-backdrop-filter: blur(4px); 706 705 opacity: 0; 707 transition: opacity 0. 2s ease;706 transition: opacity 0.32s cubic-bezier(0.22, 1, 0.36, 1); 708 707 } 709 708 … … 714 713 .hvn-theme-property-search-panel__dialog { 715 714 position: relative; 716 width: min(100%, 640px); 715 width: min(100%, 720px); 716 max-height: calc(100vh - 2rem); 717 717 margin-top: clamp(2rem, 8vh, 5rem); 718 718 padding: 1.75rem 1.5rem 1.5rem; … … 720 720 background: #fff; 721 721 box-shadow: 0 24px 60px rgba(15, 23, 42, 0.18); 722 transform: translateY(1 2px) scale(0.98);722 transform: translateY(18px) scale(0.96); 723 723 opacity: 0; 724 transition: transform 0.22s ease, opacity 0.22s ease; 724 overflow-y: auto; 725 overscroll-behavior: contain; 726 -webkit-overflow-scrolling: touch; 727 transition: 728 transform 0.32s cubic-bezier(0.22, 1, 0.36, 1), 729 opacity 0.32s cubic-bezier(0.22, 1, 0.36, 1); 725 730 } 726 731 … … 774 779 } 775 780 781 .hvn-theme-property-search-panel__field:last-child { 782 margin-bottom: 0; 783 } 784 785 .hvn-theme-property-search-panel__row .hvn-theme-property-search-panel__field { 786 margin-bottom: 0; 787 } 788 789 .hvn-theme-property-search-panel__row { 790 display: grid; 791 grid-template-columns: repeat(2, minmax(0, 1fr)); 792 gap: 1rem; 793 margin-bottom: 1rem; 794 } 795 796 .hvn-theme-property-search-panel__actions { 797 margin-top: 0.5rem; 798 } 799 776 800 .hvn-theme-property-search-panel__field label { 777 801 display: block; … … 782 806 } 783 807 784 .hvn-theme-property-search-panel__row {785 display: grid;786 grid-template-columns: repeat(2, minmax(0, 1fr));787 gap: 1rem;788 }789 790 .hvn-theme-property-search-panel__actions {791 margin-top: 0.5rem;792 }793 794 808 .hvn-theme-property-search-panel__submit { 795 809 width: 100%; … … 809 823 .hvn-theme-property-search-panel__dialog { 810 824 width: 100%; 825 max-height: none; 811 826 min-height: 100%; 812 827 margin-top: 0; 813 828 border-radius: 0; 814 829 padding: 1.25rem 1rem 1.5rem; 830 padding-top: max(1.25rem, env(safe-area-inset-top, 0px)); 831 padding-bottom: max(1.5rem, env(safe-area-inset-bottom, 0px)); 832 transform: translateY(100%); 833 } 834 835 .hvn-theme-property-search-panel.is-open .hvn-theme-property-search-panel__dialog { 836 transform: translateY(0) scale(1); 815 837 } 816 838 … … 857 879 } 858 880 881 /* Desktop dropdown — premium panel, fade + translateY reveal */ 882 .hvn-theme-header-row, 883 .hvn-theme-nav, 884 #site-navigation, 885 .hvn-theme-main-navigation { 886 overflow: visible; 887 } 888 889 .hvn-theme-nav-menu .menu-item-has-children { 890 position: relative; 891 } 892 893 .hvn-theme-nav-menu .sub-menu { 894 position: absolute; 895 top: 100%; 896 left: 0; 897 margin: 0; 898 padding: 0.5rem; 899 min-width: 240px; 900 list-style: none; 901 background: var(--hvn-theme-color-white, #fff); 902 border: 1px solid rgba(15, 23, 42, 0.08); 903 border-radius: 12px; 904 box-shadow: 905 0 4px 6px -2px rgba(15, 23, 42, 0.06), 906 0 16px 32px -8px rgba(15, 23, 42, 0.14); 907 opacity: 0; 908 visibility: hidden; 909 transform: translateY(8px); 910 pointer-events: none; 911 transition: 912 opacity 0.2s cubic-bezier(0.22, 1, 0.36, 1), 913 transform 0.2s cubic-bezier(0.22, 1, 0.36, 1), 914 visibility 0.2s; 915 z-index: 100001; 916 } 917 918 .hvn-theme-nav-menu .menu-item-has-children > .sub-menu::before { 919 content: ""; 920 position: absolute; 921 top: -14px; 922 left: 0; 923 right: 0; 924 height: 14px; 925 background: transparent; 926 } 927 928 .hvn-theme-nav-menu .menu-item-has-children:hover > .sub-menu, 929 .hvn-theme-nav-menu .menu-item-has-children.is-dropdown-open > .sub-menu, 930 .hvn-theme-nav-menu .menu-item-has-children.focus > .sub-menu, 931 .hvn-theme-nav-menu .menu-item-has-children:focus-within > .sub-menu { 932 opacity: 1; 933 visibility: visible; 934 transform: translateY(0); 935 pointer-events: auto; 936 } 937 938 .hvn-theme-nav-menu .menu-item-has-children > a::after { 939 transition: transform 0.2s cubic-bezier(0.22, 1, 0.36, 1); 940 } 941 942 .hvn-theme-nav-menu .menu-item-has-children:hover > a::after, 943 .hvn-theme-nav-menu .menu-item-has-children.is-dropdown-open > a::after, 944 .hvn-theme-nav-menu .menu-item-has-children.focus > a::after, 945 .hvn-theme-nav-menu .menu-item-has-children:focus-within > a::after { 946 transform: translateY(-50%) rotate(180deg); 947 } 948 859 949 .hvn-theme-nav-menu .sub-menu a { 860 border-left: 2px solid transparent; 861 transition: border-color 0.2s ease, color 0.2s ease, background-color 0.2s ease; 950 display: flex; 951 align-items: center; 952 min-height: 42px; 953 padding: 0.5rem 0.75rem !important; 954 font-size: 0.875rem; 955 font-weight: 500; 956 line-height: 1.35; 957 color: var(--hvn-theme-text-secondary, #50575e); 958 border-radius: 8px; 959 border-left: none; 960 transition: 961 background-color 0.18s ease, 962 color 0.18s ease; 862 963 } 863 964 864 965 .hvn-theme-nav-menu .sub-menu a:hover, 865 966 .hvn-theme-nav-menu .sub-menu a:focus-visible { 866 border-left-color: var(--hvn-theme-brand-primary, #6c60fe); 967 background-color: rgba(108, 96, 254, 0.08); 968 color: var(--hvn-theme-brand-primary, #6c60fe); 969 } 970 971 .hvn-theme-nav-menu .sub-menu .menu-item-has-children > .sub-menu { 972 top: -0.5rem; 973 left: 100%; 974 margin: 0; 975 padding: 0.5rem; 976 } 977 978 .hvn-theme-nav-menu .sub-menu .menu-item-has-children > .sub-menu::before { 979 top: 0; 980 left: -14px; 981 width: 14px; 982 height: 100%; 983 right: auto; 984 } 985 986 .hvn-theme-nav-menu .sub-menu .current-menu-item > a, 987 .hvn-theme-nav-menu .sub-menu .current-page-ancestor > a { 988 color: var(--hvn-theme-brand-primary, #6c60fe); 989 font-weight: 600; 990 background-color: rgba(108, 96, 254, 0.06); 991 } 992 993 /* Site logo — shared Customizer source */ 994 .hvn-theme-custom-logo img, 995 img.custom-logo { 996 display: block; 997 max-height: 52px; 998 width: auto; 999 height: auto; 1000 } 1001 1002 .hvn-theme-sticky-header .hvn-theme-custom-logo img, 1003 .hvn-theme-sticky-header img.custom-logo { 1004 max-height: 48px; 1005 } 1006 1007 /* Mobile drawer branding (same Site Identity logo) */ 1008 .hvn-theme-mobile-menu-branding { 1009 flex: 1; 1010 min-width: 0; 1011 padding-right: 0.75rem; 1012 } 1013 1014 .hvn-theme-mobile-menu-branding .hvn-theme-logo { 1015 margin: 0; 1016 } 1017 1018 .hvn-theme-mobile-menu-branding .hvn-theme-site-description { 1019 display: none; 1020 } 1021 1022 .hvn-theme-mobile-menu-branding .hvn-theme-custom-logo img, 1023 .hvn-theme-mobile-menu-branding img.custom-logo { 1024 max-height: 42px; 1025 } 1026 1027 .hvn-theme-mobile-menu-branding .hvn-theme-site-title { 1028 margin: 0; 1029 font-size: 1.125rem; 1030 line-height: 1.3; 1031 } 1032 1033 .hvn-theme-mobile-menu-header { 1034 gap: 0.75rem; 1035 align-items: center; 1036 } 1037 1038 /* ============================================ 1039 MOBILE MENU - FIXED: No left-to-right jump 1040 ============================================ */ 1041 1042 /* Mobile Menu Panel */ 1043 .hvn-theme-mobile-menu { 1044 position: fixed; 1045 top: 0; 1046 left: -100%; 1047 width: 85%; 1048 max-width: 320px; 1049 height: 100vh; 1050 background: var(--hvn-theme-color-white); 1051 box-shadow: 5px 0 25px rgba(0, 0, 0, 0.15); 1052 z-index: 10000; 1053 display: flex; 1054 flex-direction: column; 1055 overflow-y: auto; 1056 transition: left 0.35s cubic-bezier(0.4, 0, 0.2, 1); 1057 will-change: transform, left; 1058 } 1059 1060 .hvn-theme-mobile-menu.active { 1061 left: 0; 1062 } 1063 1064 .hvn-theme-mobile-menu-content { 1065 flex: 1; 1066 padding: 0; 1067 overflow-y: auto; 1068 -webkit-overflow-scrolling: touch; 1069 } 1070 1071 /* Mobile Navigation Menu */ 1072 .hvn-theme-mobile-nav-menu { 1073 list-style: none; 1074 margin: 0; 1075 padding: 0; 1076 } 1077 1078 .hvn-theme-mobile-nav-menu > li { 1079 border-bottom: 1px solid var(--hvn-theme-border-color-light); 1080 position: relative; 1081 } 1082 1083 .hvn-theme-mobile-nav-menu > li:last-child { 1084 border-bottom: none; 1085 } 1086 1087 /* ============================================ 1088 MOBILE MENU LINKS - NO JUMPING 1089 ============================================ */ 1090 1091 .hvn-theme-mobile-nav-menu > li > a { 1092 display: flex; 1093 align-items: center; 1094 min-height: 48px; 1095 padding: 0.75rem 1.25rem; 1096 font-size: 1rem; 1097 font-weight: 500; 1098 line-height: 1.4; 1099 color: var(--hvn-theme-text-primary); 1100 text-decoration: none; 1101 position: relative; 1102 box-sizing: border-box; 1103 width: 100%; 1104 1105 /* Smooth transitions - only color and background */ 1106 transition: 1107 color 0.3s cubic-bezier(0.4, 0, 0.2, 1), 1108 background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1); 1109 1110 /* Left border indicator - no shift */ 1111 border-left: 3px solid transparent; 1112 } 1113 1114 /* FIXED: Override theme.css padding-left change on hover */ 1115 .hvn-theme-mobile-nav-menu > li > a:hover, 1116 .hvn-theme-mobile-nav-menu > li > a:focus-visible { 1117 background-color: rgba(108, 96, 254, 0.08); 1118 color: var(--hvn-theme-brand-primary); 1119 1120 /* CRITICAL: NO padding-left change - prevents jumping */ 1121 padding-left: 1.25rem !important; 1122 1123 /* NO transform - prevents jumping */ 1124 transform: none !important; 1125 1126 /* Left border indicator */ 1127 border-left-color: var(--hvn-theme-brand-primary); 1128 } 1129 1130 /* Current menu item */ 1131 .hvn-theme-mobile-nav-menu .current-menu-item > a, 1132 .hvn-theme-mobile-nav-menu .current-page-ancestor > a { 1133 color: var(--hvn-theme-brand-primary); 1134 font-weight: 600; 1135 background-color: rgba(108, 96, 254, 0.08); 1136 border-left-color: var(--hvn-theme-brand-primary); 1137 } 1138 1139 /* ============================================ 1140 MOBILE DROPDOWN TOGGLE 1141 ============================================ */ 1142 1143 .hvn-theme-mobile-nav-menu .menu-item-has-children { 1144 position: relative; 1145 } 1146 1147 .hvn-theme-mobile-nav-menu .menu-item-has-children > a { 1148 padding-right: 3.75rem; 1149 } 1150 1151 .hvn-theme-mobile-dropdown-toggle { 1152 position: absolute; 1153 top: 2px; 1154 right: 0.75rem; 1155 display: inline-flex; 1156 align-items: center; 1157 justify-content: center; 1158 width: 44px; 1159 height: 44px; 1160 border: 1px solid var(--hvn-theme-border-color); 1161 border-radius: 8px; 1162 background: var(--hvn-theme-color-gray-100); 1163 color: var(--hvn-theme-text-primary); 1164 font-size: 0; 1165 line-height: 0; 1166 cursor: pointer; 1167 z-index: 2; 1168 transform: none; 1169 transition: 1170 background-color 0.2s ease, 1171 color 0.2s ease, 1172 border-color 0.2s ease; 1173 } 1174 1175 .hvn-theme-mobile-dropdown-toggle::before { 1176 content: ""; 1177 display: block; 1178 width: 8px; 1179 height: 8px; 1180 border-right: 2px solid currentColor; 1181 border-bottom: 2px solid currentColor; 1182 transform: rotate(45deg); 1183 transform-origin: center center; 1184 transition: transform 0.24s cubic-bezier(0.22, 1, 0.36, 1); 1185 } 1186 1187 .hvn-theme-mobile-dropdown-toggle:hover, 1188 .hvn-theme-mobile-dropdown-toggle:focus-visible { 1189 background: var(--hvn-theme-brand-primary); 1190 color: #fff; 1191 border-color: var(--hvn-theme-brand-primary); 1192 outline: 2px solid var(--hvn-theme-brand-primary); 1193 outline-offset: 2px; 1194 } 1195 1196 .hvn-theme-mobile-dropdown-toggle.active { 1197 background: rgba(108, 96, 254, 0.12); 1198 color: var(--hvn-theme-brand-primary); 1199 border-color: var(--hvn-theme-brand-primary); 1200 } 1201 1202 .hvn-theme-mobile-dropdown-toggle.active::before { 1203 transform: rotate(-135deg); 1204 } 1205 1206 /* ============================================ 1207 SUBMENU - max-height expand (no layout shift) 1208 ============================================ */ 1209 1210 .hvn-theme-mobile-nav-menu .sub-menu { 1211 display: block; 1212 max-height: 0; 1213 opacity: 0; 1214 visibility: hidden; 1215 margin: 0; 1216 padding: 0; 1217 background: var(--hvn-theme-color-gray-50, #f9fafb); 1218 border-top: 0 solid var(--hvn-theme-border-color); 1219 overflow: hidden; 1220 transition: 1221 max-height 0.28s cubic-bezier(0.22, 1, 0.36, 1), 1222 opacity 0.22s cubic-bezier(0.22, 1, 0.36, 1), 1223 visibility 0.22s, 1224 padding 0.28s cubic-bezier(0.22, 1, 0.36, 1); 1225 } 1226 1227 .hvn-theme-mobile-nav-menu .sub-menu.toggled { 1228 max-height: 1200px; 1229 opacity: 1; 1230 visibility: visible; 1231 padding: 0.125rem 0 0.625rem; 1232 border-top-width: 1px; 1233 } 1234 1235 /* ============================================ 1236 SUBMENU LINKS - NO JUMPING 1237 ============================================ */ 1238 1239 .hvn-theme-mobile-nav-menu .sub-menu a { 1240 display: flex; 1241 align-items: center; 1242 min-height: 44px; 1243 padding: 0.625rem 1.25rem 0.625rem 1.75rem !important; 1244 font-size: 0.9375rem; 1245 font-weight: 400; 1246 line-height: 1.4; 1247 color: var(--hvn-theme-text-secondary, #50575e); 1248 text-decoration: none; 1249 box-sizing: border-box; 1250 width: 100%; 1251 border-left: none; 1252 transition: 1253 color 0.2s ease, 1254 background-color 0.2s ease; 1255 } 1256 1257 .hvn-theme-mobile-nav-menu .sub-menu a:hover, 1258 .hvn-theme-mobile-nav-menu .sub-menu a:focus-visible { 1259 background-color: rgba(108, 96, 254, 0.06); 1260 color: var(--hvn-theme-brand-primary); 1261 } 1262 1263 .hvn-theme-mobile-nav-menu .sub-menu .current-menu-item > a { 1264 color: var(--hvn-theme-brand-primary); 1265 font-weight: 600; 1266 background-color: rgba(108, 96, 254, 0.08); 1267 border-left-color: var(--hvn-theme-brand-primary); 1268 } 1269 1270 /* ============================================ 1271 MOBILE OVERLAY 1272 ============================================ */ 1273 1274 .hvn-theme-mobile-overlay { 1275 position: fixed; 1276 top: 0; 1277 left: 0; 1278 width: 100%; 1279 height: 100%; 1280 background: rgba(0, 0, 0, 0.5); 1281 z-index: 9999; 1282 opacity: 0; 1283 visibility: hidden; 1284 transition: opacity 0.35s cubic-bezier(0.4, 0, 0.2, 1), 1285 visibility 0.35s cubic-bezier(0.4, 0, 0.2, 1); 1286 } 1287 1288 .hvn-theme-mobile-overlay.active { 1289 opacity: 1; 1290 visibility: visible; 1291 } 1292 1293 /* Mobile Menu CTA Section */ 1294 .hvn-theme-mobile-menu-cta { 1295 padding: 1rem 1.25rem 1.5rem; 1296 border-top: 1px solid var(--hvn-theme-border-color); 1297 margin-top: auto; 1298 flex-shrink: 0; 1299 } 1300 1301 .hvn-theme-mobile-header-cta { 1302 width: 100%; 1303 justify-content: center; 1304 transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), 1305 box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1); 1306 } 1307 1308 .hvn-theme-mobile-header-cta:hover { 1309 transform: translateY(-2px); 1310 box-shadow: 0 4px 16px rgba(108, 96, 254, 0.3); 1311 } 1312 1313 /* ============================================ 1314 MOBILE CLOSE BUTTON 1315 ============================================ */ 1316 1317 .hvn-theme-mobile-menu-close { 1318 flex-shrink: 0; 1319 width: 44px; 1320 height: 44px; 1321 display: inline-flex; 1322 align-items: center; 1323 justify-content: center; 1324 background: none; 1325 border: none; 1326 font-size: 2rem; 1327 line-height: 1; 1328 cursor: pointer; 1329 color: var(--hvn-theme-text-muted); 1330 padding: 5px 10px; 1331 transition: color 0.3s cubic-bezier(0.4, 0, 0.2, 1), 1332 transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); 1333 } 1334 1335 .hvn-theme-mobile-menu-close:hover { 1336 color: var(--hvn-theme-brand-error); 1337 transform: scale(1.1); 1338 } 1339 1340 .hvn-theme-mobile-menu-close:focus-visible { 1341 outline: 2px solid var(--hvn-theme-brand-primary); 1342 outline-offset: 2px; 1343 } 1344 1345 /* ============================================ 1346 RESPONSIVE FIXES 1347 ============================================ */ 1348 1349 @media (max-width: 480px) { 1350 .hvn-theme-mobile-menu { 1351 width: 100%; 1352 max-width: none; 1353 left: -100%; 1354 } 1355 1356 .hvn-theme-mobile-menu.active { 1357 left: 0; 1358 } 1359 1360 .hvn-theme-mobile-menu-header { 1361 padding: 12px 16px; 1362 } 1363 1364 .hvn-theme-mobile-nav-menu > li > a { 1365 padding: 0.625rem 1rem; 1366 min-height: 44px; 1367 font-size: 0.9375rem; 1368 } 1369 1370 /* FIXED: Keep padding consistent on hover */ 1371 .hvn-theme-mobile-nav-menu > li > a:hover, 1372 .hvn-theme-mobile-nav-menu > li > a:focus-visible { 1373 padding-left: 1rem !important; 1374 } 1375 1376 .hvn-theme-mobile-nav-menu .sub-menu a { 1377 padding: 0.5rem 1rem 0.5rem 1.5rem !important; 1378 min-height: 40px; 1379 font-size: 0.875rem; 1380 } 1381 1382 .hvn-theme-mobile-nav-menu .sub-menu a:hover, 1383 .hvn-theme-mobile-nav-menu .sub-menu a:focus-visible { 1384 padding: 0.5rem 1rem 0.5rem 1.5rem !important; 1385 } 1386 1387 .hvn-theme-mobile-dropdown-toggle { 1388 width: 38px; 1389 height: 38px; 1390 top: 3px; 1391 right: 0.5rem; 1392 } 1393 } 1394 1395 @media (max-width: 375px) { 1396 .hvn-theme-mobile-menu { 1397 width: 100%; 1398 max-width: none; 1399 } 1400 } 1401 1402 /* ============================================ 1403 REDUCED MOTION PREFERENCE 1404 ============================================ */ 1405 1406 @media (prefers-reduced-motion: reduce) { 1407 .hvn-theme-mobile-menu, 1408 .hvn-theme-mobile-menu-close, 1409 .hvn-theme-mobile-nav-menu > li > a, 1410 .hvn-theme-mobile-nav-menu .sub-menu a, 1411 .hvn-theme-mobile-dropdown-toggle, 1412 .hvn-theme-mobile-overlay, 1413 .hvn-theme-mobile-header-cta { 1414 transition: none !important; 1415 } 1416 1417 .hvn-theme-mobile-nav-menu > li > a:hover, 1418 .hvn-theme-mobile-nav-menu > li > a:focus-visible { 1419 padding-left: 1.25rem !important; 1420 transform: none !important; 1421 } 1422 1423 .hvn-theme-mobile-nav-menu .sub-menu a:hover, 1424 .hvn-theme-mobile-nav-menu .sub-menu a:focus-visible { 1425 padding: 0.625rem 1.25rem 0.625rem 1.75rem !important; 1426 transform: none !important; 1427 } 1428 1429 .hvn-theme-mobile-menu-close:hover { 1430 transform: none !important; 1431 } 1432 1433 .hvn-theme-mobile-header-cta:hover { 1434 transform: none !important; 1435 } 1436 1437 .hvn-theme-mobile-nav-menu .sub-menu { 1438 transition: none !important; 1439 } 867 1440 } 868 1441 … … 963 1536 964 1537 .hvn-theme-nav-menu > li > a:hover::before, 1538 .hvn-theme-nav-menu > li > a:focus-visible::before, 965 1539 .hvn-theme-nav-menu > li.current-menu-item > a::before { 966 1540 opacity: 1; 1541 } 1542 1543 .hvn-theme-nav-menu .sub-menu, 1544 .hvn-theme-nav-menu .menu-item-has-children > a::after { 1545 transition: none; 1546 } 1547 1548 .hvn-theme-nav-menu .sub-menu { 1549 transform: none; 967 1550 } 968 1551 } … … 971 1554 Responsive audit — targeted fixes (320px–1440px) 972 1555 ========================================================================== */ 1556 1557 @media (max-width: 320px) { 1558 .hvn-theme-header-row { 1559 gap: 0.35rem; 1560 } 1561 1562 .hvn-theme-custom-logo img, 1563 img.custom-logo { 1564 max-height: 36px; 1565 } 1566 1567 .hvn-theme-mobile-menu-branding .hvn-theme-custom-logo img, 1568 .hvn-theme-mobile-menu-branding img.custom-logo { 1569 max-height: 36px; 1570 } 1571 1572 .hvn-theme-mobile-nav-menu > li > a { 1573 padding-left: 1rem; 1574 padding-right: 1rem; 1575 } 1576 } 973 1577 974 1578 @media (max-width: 480px) { … … 1007 1611 padding: 1rem 0.75rem 1.25rem; 1008 1612 } 1009 1010 .hvn-theme-mobile-menu {1011 width: 100%;1012 max-width: none;1013 }1014 1613 } 1015 1614 1016 1615 @media (max-width: 767px) { 1017 1616 .hvn-theme-mobile-nav-menu .menu-item-has-children > a { 1018 padding-right: 3 rem;1617 padding-right: 3.5rem; 1019 1618 } 1020 1619 … … 1026 1625 text-align: left; 1027 1626 } 1627 1628 .hvn-realty-home .hvnly-property-card, 1629 .hvn-realty-home .hvnly-agent-card, 1630 .hvn-realty-home .hvnly-agency-card { 1631 min-width: 0; 1632 } 1633 1634 .hvn-realty-section__body { 1635 overflow-x: clip; 1636 } 1028 1637 } 1029 1638 1030 1639 @media (min-width: 768px) and (max-width: 1024px) { 1640 .hvn-theme-header-row { 1641 gap: 1rem; 1642 } 1643 1644 .hvn-theme-nav-menu > li > a { 1645 padding-left: 0.75rem; 1646 padding-right: 0.75rem; 1647 } 1648 1031 1649 .hvn-theme-footer-widgets.hvn-cols-3, 1032 1650 .hvn-theme-footer-widgets.hvn-cols-4 { 1033 1651 grid-template-columns: repeat(2, minmax(0, 1fr)); 1034 1652 } 1035 } 1036 1653 1654 .hvn-realty-dept-tabs__nav { 1655 overflow-x: auto; 1656 -webkit-overflow-scrolling: touch; 1657 scrollbar-width: thin; 1658 } 1659 } 1660 1661 @media (min-width: 1025px) and (max-width: 1440px) { 1662 .hvn-theme-nav-menu > li > a { 1663 padding-left: 0.875rem; 1664 padding-right: 0.875rem; 1665 } 1666 } -
havenlytics-realty/1.15.5/assets/css/theme.css
r334339 r334420 1941 1941 .hvn-theme-mobile-nav-menu a:focus { 1942 1942 color: var(--hvn-theme-brand-primary); 1943 padding-left: 5px; 1943 padding-left: 5px;transition: all 0.4s ease; 1944 1944 } 1945 1945 -
havenlytics-realty/1.15.5/assets/js/customizer.js
r334339 r334420 26 26 if ( node ) { 27 27 node.textContent = value; 28 } 29 } 30 31 function resolveThemeLink( value, fallback ) { 32 var link = ( value || '' ).trim(); 33 var homeUrl = ( config.homeUrl || '/' ).replace( /\/$/, '' ); 34 35 if ( ! link ) { 36 return fallback || homeUrl + '/'; 37 } 38 39 if ( /^https?:\/\//i.test( link ) ) { 40 return link; 41 } 42 43 if ( link.charAt( 0 ) === '/' ) { 44 return homeUrl + link; 45 } 46 47 return homeUrl + '/' + link.replace( /^\/+|\/+$/g, '' ) + '/'; 48 } 49 50 function setLink( selector, href, text ) { 51 var node = document.querySelector( selector ); 52 if ( ! node ) { 53 return; 54 } 55 if ( typeof href === 'string' && href ) { 56 node.setAttribute( 'href', href ); 57 } 58 if ( typeof text === 'string' ) { 59 node.textContent = text; 28 60 } 29 61 } … … 149 181 } 150 182 183 function bindHomeDepartmentButton() { 184 if ( ! wp.customize( 'hvn_realty_home_department_button_text' ) || ! wp.customize( 'hvn_realty_home_department_button_url' ) ) { 185 return; 186 } 187 188 var fallback = config.propertySearchUrl || config.homeUrl || '/'; 189 190 function refreshDepartmentButton() { 191 var text = wp.customize( 'hvn_realty_home_department_button_text' ).get(); 192 var urlValue = wp.customize( 'hvn_realty_home_department_button_url' ).get(); 193 setLink( '#hvn-realty-dept-view-all', resolveThemeLink( urlValue, fallback ), text ); 194 } 195 196 wp.customize( 'hvn_realty_home_department_button_text', function ( value ) { 197 value.bind( refreshDepartmentButton ); 198 } ); 199 200 wp.customize( 'hvn_realty_home_department_button_url', function ( value ) { 201 value.bind( refreshDepartmentButton ); 202 } ); 203 } 204 205 function bindCustomLogoPreview() { 206 if ( ! wp.customize( 'custom_logo' ) ) { 207 return; 208 } 209 210 wp.customize( 'custom_logo', function ( value ) { 211 value.bind( function ( to ) { 212 var logoId = parseInt( to, 10 ); 213 214 if ( ! logoId ) { 215 if ( wp.customize.preview ) { 216 wp.customize.preview.send( 'refresh' ); 217 } 218 return; 219 } 220 221 var attachment = wp.media.attachment( logoId ); 222 attachment.fetch().then( function () { 223 var url = attachment.get( 'url' ); 224 if ( ! url ) { 225 return; 226 } 227 228 var alt = attachment.get( 'alt' ) || ''; 229 var width = attachment.get( 'width' ); 230 var height = attachment.get( 'height' ); 231 232 document.querySelectorAll( '.hvn-theme-custom-logo img, img.custom-logo' ).forEach( function ( img ) { 233 img.src = url; 234 img.alt = alt; 235 if ( width ) { 236 img.width = width; 237 } 238 if ( height ) { 239 img.height = height; 240 } 241 } ); 242 } ); 243 } ); 244 } ); 245 } 246 151 247 function bindSiteIconPreview() { 152 248 if ( ! wp.customize( 'site_icon' ) ) { … … 356 452 357 453 bindHomeTextFields(); 454 bindHomeDepartmentButton(); 455 bindCustomLogoPreview(); 358 456 bindSiteIconPreview(); 359 457 … … 439 537 return; 440 538 } 539 height = Math.max( 40, Math.min( 100, height ) ); 441 540 setStyle( 442 541 'hvn-realty-hero-height', … … 444 543 ); 445 544 if ( typeof window.hvnRealtyInvalidateHomeMap === 'function' ) { 446 window.hvnRealtyInvalidateHomeMap(); 545 window.setTimeout( window.hvnRealtyInvalidateHomeMap, 50 ); 546 window.setTimeout( window.hvnRealtyInvalidateHomeMap, 350 ); 447 547 } 448 548 } ); … … 455 555 return; 456 556 } 557 height = Math.max( 40, Math.min( 100, height ) ); 457 558 setStyle( 458 559 'hvn-realty-hero-height-mobile', … … 460 561 ); 461 562 if ( typeof window.hvnRealtyInvalidateHomeMap === 'function' ) { 462 window.hvnRealtyInvalidateHomeMap(); 563 window.setTimeout( window.hvnRealtyInvalidateHomeMap, 50 ); 564 window.setTimeout( window.hvnRealtyInvalidateHomeMap, 350 ); 463 565 } 464 566 } ); -
havenlytics-realty/1.15.5/assets/js/havenlytics-home.js
r334339 r334420 273 273 } 274 274 275 function getHomeMapDepartmentSlugs() { 276 var $shell = $( '#hvn-realty-home-map-shell' ); 277 if ( ! $shell.length ) { 278 return []; 279 } 280 281 var raw = $shell.attr( 'data-map-departments' ); 282 if ( raw ) { 283 try { 284 var parsed = JSON.parse( raw ); 285 if ( Array.isArray( parsed ) && parsed.length ) { 286 return parsed.map( String ); 287 } 288 } catch ( error ) { 289 // Fall through to DOM lookup. 290 } 291 } 292 293 var slugs = []; 294 $shell.find( '.hvn-realty-home-map-filters input[name="hvnly_prop_depts[]"]:checked' ).each( function () { 295 slugs.push( String( $( this ).val() ) ); 296 } ); 297 298 if ( ! slugs.length ) { 299 var single = $shell.find( '.hvn-realty-home-map-filters input[name="department"]' ).val(); 300 if ( single ) { 301 slugs.push( String( single ) ); 302 } 303 } 304 305 return slugs; 306 } 307 308 /** 309 * Merge Customizer department filters into map AJAX requests. 310 * 311 * The plugin map loader prefers havenlyticsAJAX.getFormData(), which only 312 * reads the property-search form and #hvnly-filter-sidebar — not the theme 313 * hero map filter inputs. 314 */ 315 function ensureHomeMapDepartmentFilters() { 316 var slugs = getHomeMapDepartmentSlugs(); 317 if ( ! slugs.length ) { 318 return; 319 } 320 321 if ( window.havenlyticsAJAX && typeof window.havenlyticsAJAX.getFormData === 'function' ) { 322 if ( ! window.havenlyticsAJAX._hvnRealtyHomeMapFormDataPatched ) { 323 var originalGetFormData = window.havenlyticsAJAX.getFormData.bind( window.havenlyticsAJAX ); 324 325 window.havenlyticsAJAX.getFormData = function () { 326 var data = originalGetFormData(); 327 328 if ( ! $( '#hvn-realty-home-map-shell' ).length ) { 329 return data; 330 } 331 332 var homeSlugs = window.havenlyticsAJAX._hvnRealtyHomeMapDepartments || getHomeMapDepartmentSlugs(); 333 if ( ! homeSlugs.length ) { 334 return data; 335 } 336 337 data.hvnly_prop_depts = homeSlugs.slice(); 338 if ( homeSlugs.length === 1 ) { 339 data.department = homeSlugs[ 0 ]; 340 } else { 341 delete data.department; 342 } 343 344 return data; 345 }; 346 347 window.havenlyticsAJAX._hvnRealtyHomeMapFormDataPatched = true; 348 } 349 350 window.havenlyticsAJAX._hvnRealtyHomeMapDepartments = slugs; 351 } 352 } 353 275 354 function getMapInstance() { 276 355 return window.HavenlyticsPropertyMap || window.HvnlyPropertyMap || null; … … 305 384 if ( ! mapInstance || typeof mapInstance.loadPropertyMap !== 'function' ) { 306 385 return false; 386 } 387 388 var slugs = getHomeMapDepartmentSlugs(); 389 if ( slugs.length ) { 390 ensureHomeMapDepartmentFilters(); 391 392 if ( ! window.havenlyticsAJAX || typeof window.havenlyticsAJAX.getFormData !== 'function' ) { 393 return false; 394 } 307 395 } 308 396 -
havenlytics-realty/1.15.5/assets/js/theme.js
r334339 r334420 2 2 * Havenlytics Realty theme interactions (vanilla JS). 3 3 */ 4 ( function () {4 (function () { 5 5 'use strict'; 6 6 7 7 var keyboardTimeout; 8 8 9 function qs( selector, context) {10 return ( context || document ).querySelector( selector);11 } 12 13 function qsa( selector, context) {14 return Array.prototype.slice.call( ( context || document ).querySelectorAll( selector ));15 } 16 17 function on( el, event, handler) {18 if ( el) {19 el.addEventListener( event, handler);20 } 21 } 22 23 function trapFocus( container, event) {24 if ( event.key !== 'Tab' || ! container) {25 return; 26 } 27 28 var focusable = qsa( 'a, button, input, textarea, select, [tabindex]:not([tabindex="-1"])', container)29 .filter( function ( node) {30 return ! node.disabled && node.offsetParent !== null;31 } );32 33 if ( ! focusable.length) {9 function qs(selector, context) { 10 return (context || document).querySelector(selector); 11 } 12 13 function qsa(selector, context) { 14 return Array.prototype.slice.call((context || document).querySelectorAll(selector)); 15 } 16 17 function on(el, event, handler) { 18 if (el) { 19 el.addEventListener(event, handler); 20 } 21 } 22 23 function trapFocus(container, event) { 24 if (event.key !== 'Tab' || !container) { 25 return; 26 } 27 28 var focusable = qsa('a, button, input, textarea, select, [tabindex]:not([tabindex="-1"])', container) 29 .filter(function (node) { 30 return !node.disabled && node.offsetParent !== null; 31 }); 32 33 if (!focusable.length) { 34 34 return; 35 35 } 36 36 37 37 var first = focusable[0]; 38 var last = focusable[ focusable.length - 1];39 40 if ( event.shiftKey && document.activeElement === first) {38 var last = focusable[focusable.length - 1]; 39 40 if (event.shiftKey && document.activeElement === first) { 41 41 event.preventDefault(); 42 42 last.focus(); 43 } else if ( ! event.shiftKey && document.activeElement === last) {43 } else if (!event.shiftKey && document.activeElement === last) { 44 44 event.preventDefault(); 45 45 first.focus(); … … 47 47 } 48 48 49 function handleActivation( event) {50 if ( event.key === 'Enter' || event.key === ' ') {49 function handleActivation(event) { 50 if (event.key === 'Enter' || event.key === ' ') { 51 51 event.preventDefault(); 52 52 event.currentTarget.click(); … … 54 54 } 55 55 56 function handleFirstTab( event) {57 if ( event.key === 'Tab') {58 document.body.classList.remove( 'mouse-user');59 document.body.classList.add( 'keyboard-user');60 clearTimeout( keyboardTimeout);61 keyboardTimeout = setTimeout( function () {62 document.body.classList.remove( 'keyboard-user');63 document.body.classList.add( 'mouse-user');64 }, 1000 );56 function handleFirstTab(event) { 57 if (event.key === 'Tab') { 58 document.body.classList.remove('mouse-user'); 59 document.body.classList.add('keyboard-user'); 60 clearTimeout(keyboardTimeout); 61 keyboardTimeout = setTimeout(function () { 62 document.body.classList.remove('keyboard-user'); 63 document.body.classList.add('mouse-user'); 64 }, 1000); 65 65 } 66 66 } 67 67 68 68 function initKeyboardMode() { 69 window.addEventListener( 'keydown', handleFirstTab);70 document.body.classList.add( 'mouse-user');69 window.addEventListener('keydown', handleFirstTab); 70 document.body.classList.add('mouse-user'); 71 71 } 72 72 73 73 function initDesktopNavigation() { 74 var desktopMenu = qs( '#site-navigation');75 if ( ! desktopMenu) {76 return; 77 } 78 79 var subMenus = qsa( '.menu-item-has-children', desktopMenu);80 81 subMenus.forEach( function ( item ) { 82 item.setAttribute( 'aria-haspopup', 'true' );83 84 var parentLink = qs( 'a', item ); 74 var desktopMenu = qs('#site-navigation'); 75 if (!desktopMenu) { 76 return; 77 } 78 79 var subMenus = qsa('.menu-item-has-children', desktopMenu); 80 var closeTimers = new WeakMap(); 81 82 function setDropdownExpanded( item, expanded ) { 83 var parentLink = qs( ':scope > a', item ); 84 85 85 if ( parentLink ) { 86 on( parentLink, 'keydown', function ( event ) { 87 if ( event.key !== 'ArrowDown' && event.key !== 'ArrowUp' ) { 86 parentLink.setAttribute( 'aria-expanded', expanded ? 'true' : 'false' ); 87 } 88 89 item.classList.toggle( 'is-dropdown-open', !! expanded ); 90 } 91 92 subMenus.forEach(function (item) { 93 item.setAttribute('aria-haspopup', 'true'); 94 95 var parentLink = qs(':scope > a', item); 96 if (parentLink) { 97 parentLink.setAttribute('aria-expanded', 'false'); 98 99 on(item, 'mouseenter', function () { 100 var timer = closeTimers.get(item); 101 if (timer) { 102 clearTimeout(timer); 103 closeTimers.delete(item); 104 } 105 setDropdownExpanded(item, true); 106 }); 107 108 on(item, 'mouseleave', function () { 109 var timer = setTimeout(function () { 110 if (!item.contains(document.activeElement)) { 111 setDropdownExpanded(item, false); 112 } 113 }, 160); 114 closeTimers.set(item, timer); 115 }); 116 117 on(parentLink, 'keydown', function (event) { 118 if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') { 119 if (event.key === 'Escape') { 120 setDropdownExpanded(item, false); 121 item.classList.remove('focus'); 122 } 88 123 return; 89 124 } 90 125 event.preventDefault(); 91 var subMenu = qs( '.sub-menu', item ); 92 if ( subMenu && subMenu.children.length ) { 93 item.classList.add( 'focus' ); 94 var firstLink = qs( 'a', subMenu ); 95 if ( firstLink ) { 126 var subMenu = qs('.sub-menu', item); 127 if (subMenu && subMenu.children.length) { 128 item.classList.add('focus'); 129 setDropdownExpanded(item, true); 130 var firstLink = qs('a', subMenu); 131 if (firstLink) { 96 132 firstLink.focus(); 97 133 } 98 134 } 99 } ); 100 } 101 } ); 102 103 qsa( '.sub-menu a', desktopMenu ).forEach( function ( link ) { 104 on( link, 'keydown', function ( event ) { 105 if ( event.key === 'ArrowLeft' ) { 106 var parent = link.closest( '.sub-menu' ); 107 var parentItem = parent ? parent.closest( '.menu-item-has-children' ) : null; 108 if ( parentItem ) { 109 var parentLink = qs( 'a', parentItem ); 110 parentItem.classList.remove( 'focus' ); 111 if ( parentLink ) { 135 }); 136 } 137 }); 138 139 qsa('.sub-menu a', desktopMenu).forEach(function (link) { 140 on(link, 'keydown', function (event) { 141 if (event.key === 'ArrowLeft') { 142 var parent = link.closest('.sub-menu'); 143 var parentItem = parent ? parent.closest('.menu-item-has-children') : null; 144 if (parentItem) { 145 var parentLink = qs(':scope > a', parentItem); 146 parentItem.classList.remove('focus'); 147 setDropdownExpanded(parentItem, false); 148 if (parentLink) { 112 149 parentLink.focus(); 113 150 } … … 115 152 } 116 153 117 if ( event.key === 'ArrowRight' ) { 118 var currentItem = link.closest( '.menu-item-has-children' ); 119 if ( currentItem ) { 120 var childSubMenu = qs( '.sub-menu', currentItem ); 121 if ( childSubMenu && childSubMenu.children.length ) { 122 currentItem.classList.add( 'focus' ); 123 var firstChildLink = qs( 'a', childSubMenu ); 124 if ( firstChildLink ) { 154 if (event.key === 'ArrowRight') { 155 var currentItem = link.closest('.menu-item-has-children'); 156 if (currentItem) { 157 var childSubMenu = qs('.sub-menu', currentItem); 158 if (childSubMenu && childSubMenu.children.length) { 159 currentItem.classList.add('focus'); 160 setDropdownExpanded(currentItem, true); 161 var firstChildLink = qs('a', childSubMenu); 162 if (firstChildLink) { 125 163 firstChildLink.focus(); 126 164 } … … 129 167 } 130 168 131 if ( event.key === 'Escape' ) { 132 var closestMenu = link.closest( '.hvn-theme-nav-menu' ); 133 if ( closestMenu ) { 134 qsa( '.menu-item-has-children.focus', closestMenu ).forEach( function ( openItem ) { 135 openItem.classList.remove( 'focus' ); 136 } ); 137 var mainLinks = qsa( 'a', closestMenu ); 138 if ( mainLinks[0] ) { 169 if (event.key === 'Escape') { 170 var closestMenu = link.closest('.hvn-theme-nav-menu'); 171 if (closestMenu) { 172 qsa('.menu-item-has-children.focus, .menu-item-has-children.is-dropdown-open', closestMenu).forEach(function (openItem) { 173 openItem.classList.remove('focus'); 174 setDropdownExpanded(openItem, false); 175 }); 176 var mainLinks = qsa('a', closestMenu); 177 if (mainLinks[0]) { 139 178 mainLinks[0].focus(); 140 179 } 141 180 } 142 181 } 143 } );144 } );145 146 qsa( 'a', desktopMenu ).forEach( function ( link) {147 on( link, 'focus', function () {182 }); 183 }); 184 185 qsa('a', desktopMenu).forEach(function (link) { 186 on(link, 'focus', function () { 148 187 var node = link.parentNode; 149 while ( node && node !== desktopMenu ) { 150 if ( node.tagName === 'LI' && node.classList.contains( 'menu-item-has-children' ) ) { 151 node.classList.add( 'focus' ); 188 while (node && node !== desktopMenu) { 189 if (node.tagName === 'LI' && node.classList.contains('menu-item-has-children')) { 190 node.classList.add('focus'); 191 setDropdownExpanded(node, true); 152 192 } 153 193 node = node.parentNode; 154 194 } 155 } );156 157 on( link, 'blur', function () {195 }); 196 197 on(link, 'blur', function () { 158 198 var parent = link.parentNode; 159 setTimeout( function () { 160 if ( parent && parent.classList && parent.classList.contains( 'menu-item-has-children' ) ) { 161 if ( ! parent.contains( document.activeElement ) ) { 162 parent.classList.remove( 'focus' ); 163 } 164 } 165 }, 10 ); 166 } ); 167 } ); 199 setTimeout(function () { 200 if (parent && parent.classList && parent.classList.contains('menu-item-has-children')) { 201 if (!parent.contains(document.activeElement)) { 202 parent.classList.remove('focus'); 203 setDropdownExpanded(parent, false); 204 } 205 } 206 }, 10); 207 }); 208 }); 168 209 } 169 210 170 211 function initMobileMenu() { 171 var menuToggle = qs( '.hvn-theme-menu-toggle');172 var mobileMenu = qs( '#hvn-mobile-menu');173 var mobileOverlay = qs( '.hvn-theme-mobile-overlay');174 var mobileClose = qs( '.hvn-theme-mobile-menu-close');175 176 if ( ! menuToggle || ! mobileMenu) {212 var menuToggle = qs('.hvn-theme-menu-toggle'); 213 var mobileMenu = qs('#hvn-mobile-menu'); 214 var mobileOverlay = qs('.hvn-theme-mobile-overlay'); 215 var mobileClose = qs('.hvn-theme-mobile-menu-close'); 216 217 if (!menuToggle || !mobileMenu) { 177 218 return; 178 219 } 179 220 180 221 function openMobileMenu() { 181 mobileMenu.classList.add( 'active');182 mobileMenu.setAttribute( 'aria-hidden', 'false');183 if ( mobileOverlay) {184 mobileOverlay.classList.add( 'active');185 mobileOverlay.setAttribute( 'aria-hidden', 'false');186 } 187 menuToggle.classList.add( 'active');188 menuToggle.setAttribute( 'aria-expanded', 'true');222 mobileMenu.classList.add('active'); 223 mobileMenu.setAttribute('aria-hidden', 'false'); 224 if (mobileOverlay) { 225 mobileOverlay.classList.add('active'); 226 mobileOverlay.setAttribute('aria-hidden', 'false'); 227 } 228 menuToggle.classList.add('active'); 229 menuToggle.setAttribute('aria-expanded', 'true'); 189 230 document.body.style.overflow = 'hidden'; 190 setTimeout( function () {191 if ( mobileClose) {231 setTimeout(function () { 232 if (mobileClose) { 192 233 mobileClose.focus(); 193 234 } 194 }, 100 );235 }, 100); 195 236 } 196 237 197 238 function closeMobileMenu() { 198 mobileMenu.classList.remove( 'active');199 mobileMenu.setAttribute( 'aria-hidden', 'true');200 if ( mobileOverlay) {201 mobileOverlay.classList.remove( 'active');202 mobileOverlay.setAttribute( 'aria-hidden', 'true');203 } 204 menuToggle.classList.remove( 'active');205 menuToggle.setAttribute( 'aria-expanded', 'false');239 mobileMenu.classList.remove('active'); 240 mobileMenu.setAttribute('aria-hidden', 'true'); 241 if (mobileOverlay) { 242 mobileOverlay.classList.remove('active'); 243 mobileOverlay.setAttribute('aria-hidden', 'true'); 244 } 245 menuToggle.classList.remove('active'); 246 menuToggle.setAttribute('aria-expanded', 'false'); 206 247 document.body.style.overflow = ''; 248 qsa('.hvn-theme-mobile-nav-menu .sub-menu.toggled').forEach(function (subMenu) { 249 subMenu.classList.remove('toggled'); 250 subMenu.setAttribute('aria-hidden', 'true'); 251 }); 252 qsa('.hvn-theme-mobile-dropdown-toggle.active').forEach(function (btn) { 253 btn.classList.remove('active'); 254 btn.setAttribute('aria-expanded', 'false'); 255 }); 256 qsa('.hvn-theme-mobile-nav-menu .menu-item-has-children > a[aria-expanded="true"]').forEach(function (link) { 257 link.setAttribute('aria-expanded', 'false'); 258 }); 207 259 menuToggle.focus(); 208 260 } 209 261 210 on( menuToggle, 'click', function ( event) {262 on(menuToggle, 'click', function (event) { 211 263 event.preventDefault(); 212 if ( mobileMenu.classList.contains( 'active' )) {264 if (mobileMenu.classList.contains('active')) { 213 265 closeMobileMenu(); 214 266 } else { 215 267 openMobileMenu(); 216 268 } 217 } );218 on( menuToggle, 'keydown', handleActivation);219 220 if ( mobileClose) {221 on( mobileClose, 'click', function ( event) {269 }); 270 on(menuToggle, 'keydown', handleActivation); 271 272 if (mobileClose) { 273 on(mobileClose, 'click', function (event) { 222 274 event.preventDefault(); 223 275 closeMobileMenu(); 224 } );225 on( mobileClose, 'keydown', handleActivation);226 } 227 228 if ( mobileOverlay) {229 on( mobileOverlay, 'click', closeMobileMenu);230 } 231 232 on( document, 'keydown', function ( event) {233 if ( event.key === 'Escape' && mobileMenu.classList.contains( 'active' )) {276 }); 277 on(mobileClose, 'keydown', handleActivation); 278 } 279 280 if (mobileOverlay) { 281 on(mobileOverlay, 'click', closeMobileMenu); 282 } 283 284 on(document, 'keydown', function (event) { 285 if (event.key === 'Escape' && mobileMenu.classList.contains('active')) { 234 286 closeMobileMenu(); 235 287 } 236 } );237 238 on( window, 'resize', function () {239 if ( window.innerWidth > 991) {288 }); 289 290 on(window, 'resize', function () { 291 if (window.innerWidth > 991) { 240 292 closeMobileMenu(); 241 293 } 242 } ); 243 244 on( mobileMenu, 'keydown', function ( event ) { 245 trapFocus( mobileMenu, event ); 246 } ); 247 248 qsa( '.hvn-theme-mobile-nav-menu .menu-item-has-children' ).forEach( function ( item ) { 249 var subMenu = qs( ':scope > .sub-menu', item ); 250 if ( ! subMenu || qs( '.hvn-theme-mobile-dropdown-toggle', item ) ) { 294 }); 295 296 on(mobileMenu, 'keydown', function (event) { 297 trapFocus(mobileMenu, event); 298 }); 299 300 // Initialize mobile dropdown toggles 301 qsa('.hvn-theme-mobile-nav-menu .menu-item-has-children').forEach(function (item, index) { 302 var subMenu = qs(':scope > .sub-menu', item); 303 if (!subMenu || qs('.hvn-theme-mobile-dropdown-toggle', item)) { 251 304 return; 252 305 } 253 306 254 var toggle = document.createElement( 'button' ); 307 var subMenuId = subMenu.id || ('hvn-mobile-submenu-' + index); 308 subMenu.id = subMenuId; 309 subMenu.setAttribute('aria-hidden', 'true'); 310 311 var toggle = document.createElement('button'); 255 312 toggle.className = 'hvn-theme-mobile-dropdown-toggle'; 256 313 toggle.type = 'button'; 257 toggle.setAttribute( 'aria-expanded', 'false' ); 258 toggle.innerHTML = '▼'; 259 item.appendChild( toggle ); 260 261 on( toggle, 'click', function ( event ) { 314 toggle.setAttribute('aria-expanded', 'false'); 315 toggle.setAttribute('aria-controls', subMenuId); 316 toggle.setAttribute('aria-label', 'Toggle submenu'); 317 item.appendChild(toggle); 318 319 on(toggle, 'click', function (event) { 262 320 event.preventDefault(); 263 321 event.stopPropagation(); 264 var expanded = toggle.getAttribute( 'aria-expanded' ) === 'true'; 265 toggle.setAttribute( 'aria-expanded', expanded ? 'false' : 'true' ); 266 toggle.classList.toggle( 'active', ! expanded ); 267 subMenu.classList.toggle( 'toggled', ! expanded ); 268 subMenu.style.display = expanded ? 'none' : 'block'; 269 } ); 270 271 on( toggle, 'keydown', handleActivation ); 272 } ); 322 var expanded = toggle.getAttribute('aria-expanded') === 'true'; 323 var isExpanded = !expanded; 324 toggle.setAttribute('aria-expanded', isExpanded ? 'true' : 'false'); 325 toggle.classList.toggle('active', isExpanded); 326 327 var parentLink = qs(':scope > a', item); 328 if (parentLink) { 329 parentLink.setAttribute('aria-expanded', isExpanded ? 'true' : 'false'); 330 } 331 332 subMenu.setAttribute('aria-hidden', isExpanded ? 'false' : 'true'); 333 subMenu.classList.toggle('toggled', isExpanded); 334 }); 335 336 on(toggle, 'keydown', handleActivation); 337 }); 338 339 // Handle submenu toggle from parent link 340 qsa('.hvn-theme-mobile-nav-menu .menu-item-has-children > a').forEach(function (link) { 341 var item = link.parentNode; 342 var subMenu = qs(':scope > .sub-menu', item); 343 var toggle = qs('.hvn-theme-mobile-dropdown-toggle', item); 344 345 // Allow enter/space on the parent link to toggle if submenu exists 346 on(link, 'keydown', function (event) { 347 if ((event.key === 'Enter' || event.key === ' ') && subMenu && toggle) { 348 event.preventDefault(); 349 toggle.click(); 350 } 351 }); 352 }); 273 353 } 274 354 275 355 function initSkipLink() { 276 var skipLink = qs( '.skip-link');277 if ( ! skipLink) {278 return; 279 } 280 281 on( skipLink, 'keydown', function ( event) {282 if ( event.key === 'Enter') {283 var target = skipLink.getAttribute( 'href');284 if ( target) {285 var node = qs( target);286 if ( node) {287 node.setAttribute( 'tabindex', '-1');356 var skipLink = qs('.skip-link'); 357 if (!skipLink) { 358 return; 359 } 360 361 on(skipLink, 'keydown', function (event) { 362 if (event.key === 'Enter') { 363 var target = skipLink.getAttribute('href'); 364 if (target) { 365 var node = qs(target); 366 if (node) { 367 node.setAttribute('tabindex', '-1'); 288 368 node.focus(); 289 369 } 290 370 } 291 371 } 292 } );372 }); 293 373 } 294 374 295 375 function initScrollTop() { 296 var scrollTopBtn = document.getElementById( 'hvn-scroll-top' ) || document.querySelector( '.hvn-theme-back-to-top');297 if ( ! scrollTopBtn) {298 return; 299 } 300 301 on( window, 'scroll', function () {302 if ( window.scrollY > 300) {303 scrollTopBtn.classList.add( 'show');376 var scrollTopBtn = document.getElementById('hvn-scroll-top') || document.querySelector('.hvn-theme-back-to-top'); 377 if (!scrollTopBtn) { 378 return; 379 } 380 381 on(window, 'scroll', function () { 382 if (window.scrollY > 300) { 383 scrollTopBtn.classList.add('show'); 304 384 } else { 305 scrollTopBtn.classList.remove( 'show');306 } 307 } );308 309 function scrollToTop( event) {310 if ( event) {385 scrollTopBtn.classList.remove('show'); 386 } 387 }); 388 389 function scrollToTop(event) { 390 if (event) { 311 391 event.preventDefault(); 312 392 } 313 window.scrollTo( { top: 0, behavior: 'smooth' });314 } 315 316 on( scrollTopBtn, 'click', scrollToTop);317 on( scrollTopBtn, 'keydown', function ( event) {318 if ( event.key === 'Enter' || event.key === ' ') {319 scrollToTop( event);320 } 321 } );393 window.scrollTo({ top: 0, behavior: 'smooth' }); 394 } 395 396 on(scrollTopBtn, 'click', scrollToTop); 397 on(scrollTopBtn, 'keydown', function (event) { 398 if (event.key === 'Enter' || event.key === ' ') { 399 scrollToTop(event); 400 } 401 }); 322 402 } 323 403 324 404 function initHeaderPropertySearch() { 325 var panel = qs( '#hvn-property-search-panel');326 var openBtn = qs( '[data-hvn-search-open]');327 328 if ( ! panel || ! openBtn) {329 return; 330 } 331 332 var dialog = qs( '.hvn-theme-property-search-panel__dialog', panel);333 var closeTriggers = qsa( '[data-hvn-search-close]', panel);334 var keywordField = qs( '#hvn-header-address-keyword', panel);405 var panel = qs('#hvn-property-search-panel'); 406 var openBtn = qs('[data-hvn-search-open]'); 407 408 if (!panel || !openBtn) { 409 return; 410 } 411 412 var dialog = qs('.hvn-theme-property-search-panel__dialog', panel); 413 var closeTriggers = qsa('[data-hvn-search-close]', panel); 414 var keywordField = qs('#hvn-header-address-keyword', panel); 335 415 336 416 function openPanel() { 337 417 panel.hidden = false; 338 panel.setAttribute( 'aria-hidden', 'false' ); 339 panel.classList.add( 'is-open' ); 340 openBtn.setAttribute( 'aria-expanded', 'true' ); 341 document.body.classList.add( 'hvn-search-panel-open' ); 418 panel.setAttribute('aria-hidden', 'false'); 419 openBtn.setAttribute('aria-expanded', 'true'); 420 document.body.classList.add('hvn-search-panel-open'); 342 421 document.body.style.overflow = 'hidden'; 343 422 344 setTimeout( function () { 345 if ( keywordField ) { 423 requestAnimationFrame(function () { 424 requestAnimationFrame(function () { 425 panel.classList.add('is-open'); 426 }); 427 }); 428 429 window.setTimeout(function () { 430 if (keywordField) { 346 431 keywordField.focus(); 347 432 } 348 }, 100);433 }, 320); 349 434 } 350 435 351 436 function closePanel() { 352 panel.classList.remove( 'is-open');353 panel.setAttribute( 'aria-hidden', 'true');354 openBtn.setAttribute( 'aria-expanded', 'false');355 document.body.classList.remove( 'hvn-search-panel-open');437 panel.classList.remove('is-open'); 438 panel.setAttribute('aria-hidden', 'true'); 439 openBtn.setAttribute('aria-expanded', 'false'); 440 document.body.classList.remove('hvn-search-panel-open'); 356 441 document.body.style.overflow = ''; 357 442 openBtn.focus(); 358 443 359 setTimeout(function () {444 window.setTimeout(function () { 360 445 panel.hidden = true; 361 }, 200);362 } 363 364 on( openBtn, 'click', function ( event) {446 }, 320); 447 } 448 449 on(openBtn, 'click', function (event) { 365 450 event.preventDefault(); 366 if ( panel.classList.contains( 'is-open' )) {451 if (panel.classList.contains('is-open')) { 367 452 closePanel(); 368 453 } else { 369 454 openPanel(); 370 455 } 371 } );372 373 closeTriggers.forEach( function ( trigger) {374 on( trigger, 'click', function ( event) {456 }); 457 458 closeTriggers.forEach(function (trigger) { 459 on(trigger, 'click', function (event) { 375 460 event.preventDefault(); 376 461 closePanel(); 377 } );378 } );379 380 on( document, 'keydown', function ( event) {381 if ( event.key === 'Escape' && panel.classList.contains( 'is-open' )) {462 }); 463 }); 464 465 on(document, 'keydown', function (event) { 466 if (event.key === 'Escape' && panel.classList.contains('is-open')) { 382 467 closePanel(); 383 468 } 384 } );385 386 if ( dialog) {387 on( dialog, 'keydown', function ( event) {388 trapFocus( dialog, event);389 } );469 }); 470 471 if (dialog) { 472 on(dialog, 'keydown', function (event) { 473 trapFocus(dialog, event); 474 }); 390 475 } 391 476 } 392 477 393 478 function initPreloader() { 394 var preloader = qs( '#hvn-theme-preloader');395 if ( ! preloader) {479 var preloader = qs('#hvn-theme-preloader'); 480 if (!preloader) { 396 481 return; 397 482 } 398 483 399 484 function hidePreloader() { 400 preloader.classList.add( 'is-hidden');401 preloader.setAttribute( 'aria-hidden', 'true');402 document.body.classList.remove( 'hvn-theme-is-loading');403 document.body.classList.add( 'hvn-theme-is-loaded');404 405 setTimeout( function () {406 if ( preloader.parentNode) {407 preloader.parentNode.removeChild( preloader);408 } 409 }, 450 );410 } 411 412 if ( document.readyState === 'complete') {485 preloader.classList.add('is-hidden'); 486 preloader.setAttribute('aria-hidden', 'true'); 487 document.body.classList.remove('hvn-theme-is-loading'); 488 document.body.classList.add('hvn-theme-is-loaded'); 489 490 setTimeout(function () { 491 if (preloader.parentNode) { 492 preloader.parentNode.removeChild(preloader); 493 } 494 }, 450); 495 } 496 497 if (document.readyState === 'complete') { 413 498 hidePreloader(); 414 499 } else { 415 on( window, 'load', hidePreloader);500 on(window, 'load', hidePreloader); 416 501 } 417 502 } … … 427 512 } 428 513 429 if ( document.readyState === 'loading') {430 document.addEventListener( 'DOMContentLoaded', init);514 if (document.readyState === 'loading') { 515 document.addEventListener('DOMContentLoaded', init); 431 516 } else { 432 517 init(); 433 518 } 434 } )();519 })(); -
havenlytics-realty/1.15.5/functions.php
r334339 r334420 7 7 8 8 if ( ! defined( 'HVN_REALTY_VERSION' ) ) { 9 define( 'HVN_REALTY_VERSION', '1.15. 0' );9 define( 'HVN_REALTY_VERSION', '1.15.5' ); 10 10 } 11 11 … … 29 29 add_image_size( 'hvn-realty-blog', 800, 450, true ); 30 30 add_image_size( 'hvn-realty-featured', 1200, 600, true ); 31 add_post_type_support( 'hvnly_property', 'thumbnail' ); 31 32 if ( post_type_exists( 'hvnly_property' ) ) { 33 add_post_type_support( 'hvnly_property', 'thumbnail' ); 34 } 32 35 33 36 … … 121 124 122 125 function hvn_realty_font_preconnect() { 123 if ( is_admin() ) {126 if ( is_admin() || ! function_exists( 'hvn_realty_get_google_font_families' ) ) { 124 127 return; 125 128 } … … 154 157 function hvn_realty_scripts() { 155 158 $font_deps = array(); 156 $font_handle = hvn_realty_enqueue_google_fonts(); 157 if ( $font_handle ) { 158 $font_deps[] = $font_handle; 159 } 160 161 wp_enqueue_style( 162 'hvn-realty-theme', 163 HVN_REALTY_TEMPLATE_URL . '/assets/css/theme.css', 164 $font_deps, 165 HVN_REALTY_VERSION 166 ); 167 168 wp_enqueue_style( 169 'hvn-realty-polish', 170 HVN_REALTY_TEMPLATE_URL . '/assets/css/polish.css', 171 array( 'hvn-realty-theme' ), 172 HVN_REALTY_VERSION 173 ); 174 175 wp_enqueue_style( 176 'hvn-realty-layouts', 177 HVN_REALTY_TEMPLATE_URL . '/assets/css/layouts.css', 178 array( 'hvn-realty-polish' ), 179 HVN_REALTY_VERSION 180 ); 181 182 wp_enqueue_style( 183 'hvn-realty-style', 184 get_stylesheet_uri(), 185 array( 'hvn-realty-layouts' ), 186 HVN_REALTY_VERSION 187 ); 188 189 wp_enqueue_script( 190 'hvn-realty-theme-js', 191 HVN_REALTY_TEMPLATE_URL . '/assets/js/theme.js', 192 array(), 193 HVN_REALTY_VERSION, 194 true 195 ); 159 160 if ( function_exists( 'hvn_realty_enqueue_google_fonts' ) ) { 161 $font_handle = hvn_realty_enqueue_google_fonts(); 162 if ( $font_handle ) { 163 $font_deps[] = $font_handle; 164 } 165 } 166 167 if ( function_exists( 'hvn_realty_enqueue_theme_style' ) ) { 168 hvn_realty_enqueue_theme_style( 'hvn-realty-theme', 'assets/css/theme.css', $font_deps ); 169 hvn_realty_enqueue_theme_style( 'hvn-realty-polish', 'assets/css/polish.css', array( 'hvn-realty-theme' ) ); 170 hvn_realty_enqueue_theme_style( 'hvn-realty-layouts', 'assets/css/layouts.css', array( 'hvn-realty-polish' ) ); 171 hvn_realty_enqueue_theme_style( 'hvn-realty-style', 'style.css', array( 'hvn-realty-layouts' ) ); 172 } else { 173 wp_enqueue_style( 'hvn-realty-theme', HVN_REALTY_TEMPLATE_URL . '/assets/css/theme.css', $font_deps, HVN_REALTY_VERSION ); 174 wp_enqueue_style( 'hvn-realty-polish', HVN_REALTY_TEMPLATE_URL . '/assets/css/polish.css', array( 'hvn-realty-theme' ), HVN_REALTY_VERSION ); 175 wp_enqueue_style( 'hvn-realty-layouts', HVN_REALTY_TEMPLATE_URL . '/assets/css/layouts.css', array( 'hvn-realty-polish' ), HVN_REALTY_VERSION ); 176 wp_enqueue_style( 'hvn-realty-style', get_stylesheet_uri(), array( 'hvn-realty-layouts' ), HVN_REALTY_VERSION ); 177 } 178 179 if ( function_exists( 'hvn_realty_enqueue_theme_script' ) ) { 180 hvn_realty_enqueue_theme_script( 'hvn-realty-theme-js', 'assets/js/theme.js' ); 181 } else { 182 wp_enqueue_script( 'hvn-realty-theme-js', HVN_REALTY_TEMPLATE_URL . '/assets/js/theme.js', array(), HVN_REALTY_VERSION, true ); 183 } 196 184 197 185 wp_localize_script( … … 224 212 225 213 if ( is_page() || is_404() ) { 226 wp_enqueue_style( 227 'hvn-realty-page', 228 HVN_REALTY_TEMPLATE_URL . '/assets/css/page.css', 229 array( 'hvn-realty-layouts' ), 230 HVN_REALTY_VERSION 231 ); 214 if ( function_exists( 'hvn_realty_enqueue_theme_style' ) ) { 215 hvn_realty_enqueue_theme_style( 'hvn-realty-page', 'assets/css/page.css', array( 'hvn-realty-layouts' ) ); 216 } else { 217 wp_enqueue_style( 'hvn-realty-page', HVN_REALTY_TEMPLATE_URL . '/assets/css/page.css', array( 'hvn-realty-layouts' ), HVN_REALTY_VERSION ); 218 } 232 219 } 233 220 234 221 if ( is_home() || is_archive() || is_search() ) { 235 wp_enqueue_style( 236 'hvn-realty-blog', 237 HVN_REALTY_TEMPLATE_URL . '/assets/css/blog.css', 238 array( 'hvn-realty-layouts' ), 239 HVN_REALTY_VERSION 240 ); 222 if ( function_exists( 'hvn_realty_enqueue_theme_style' ) ) { 223 hvn_realty_enqueue_theme_style( 'hvn-realty-blog', 'assets/css/blog.css', array( 'hvn-realty-layouts' ) ); 224 } else { 225 wp_enqueue_style( 'hvn-realty-blog', HVN_REALTY_TEMPLATE_URL . '/assets/css/blog.css', array( 'hvn-realty-layouts' ), HVN_REALTY_VERSION ); 226 } 241 227 } 242 228 243 229 if ( is_single() ) { 244 wp_enqueue_style( 245 'hvn-realty-single', 246 HVN_REALTY_TEMPLATE_URL . '/assets/css/single.css', 247 array( 'hvn-realty-layouts' ), 248 HVN_REALTY_VERSION 249 ); 230 if ( function_exists( 'hvn_realty_enqueue_theme_style' ) ) { 231 hvn_realty_enqueue_theme_style( 'hvn-realty-single', 'assets/css/single.css', array( 'hvn-realty-layouts' ) ); 232 } else { 233 wp_enqueue_style( 'hvn-realty-single', HVN_REALTY_TEMPLATE_URL . '/assets/css/single.css', array( 'hvn-realty-layouts' ), HVN_REALTY_VERSION ); 234 } 250 235 } 251 236 } … … 256 241 */ 257 242 function hvn_realty_editor_fonts() { 258 hvn_realty_enqueue_google_fonts(); 243 if ( function_exists( 'hvn_realty_enqueue_google_fonts' ) ) { 244 hvn_realty_enqueue_google_fonts(); 245 } 259 246 } 260 247 add_action( 'enqueue_block_editor_assets', 'hvn_realty_editor_fonts', 5 ); … … 487 474 * Load required files. 488 475 */ 489 require get_template_directory() . '/inc/customizer/loader.php'; 490 require get_template_directory() . '/inc/plugin-hooks.php'; 491 require get_template_directory() . '/inc/integrations/havenlytics/bootstrap.php'; 492 require get_template_directory() . '/inc/setup/theme-launch.php'; 493 require get_template_directory() . '/inc/setup/theme-menus.php'; 494 require get_template_directory() . '/inc/setup/theme-footer-widgets.php'; 495 require get_template_directory() . '/inc/elementor.php'; 496 require get_template_directory() . '/inc/custom-header.php'; 497 require get_template_directory() . '/inc/template-tags.php'; 498 require get_template_directory() . '/inc/layout.php'; 499 require get_template_directory() . '/inc/template-functions.php'; 500 require get_template_directory() . '/inc/customizer.php'; 501 require get_template_directory() . '/inc/breadcrumbs.php'; 502 require get_template_directory() . '/inc/admin/realty-admin.php'; 503 require get_template_directory() . '/inc/admin/theme-welcome-notice.php'; 476 $theme_loader = get_template_directory() . '/inc/theme-loader.php'; 477 if ( file_exists( $theme_loader ) ) { 478 require_once $theme_loader; 479 } else { 480 /** 481 * Minimal fallback when theme-loader.php is missing from the package. 482 * 483 * @param string $relative_path Theme-relative file path. 484 * @param bool $optional Unused in fallback. 485 * @return bool 486 */ 487 function hvn_realty_load_theme_file( $relative_path, $optional = true ) { 488 $file = get_template_directory() . '/' . ltrim( $relative_path, '/' ); 489 if ( file_exists( $file ) ) { 490 require_once $file; 491 return true; 492 } 493 return false; 494 } 495 } 496 497 hvn_realty_load_theme_file( 'inc/customizer/loader.php', false ); 498 hvn_realty_load_theme_file( 'inc/plugin-hooks.php', false ); 499 500 if ( ! hvn_realty_load_theme_file( 'inc/integrations/havenlytics/bootstrap.php', true ) ) { 501 if ( function_exists( 'hvn_realty_missing_integration_notice' ) ) { 502 add_action( 'admin_notices', 'hvn_realty_missing_integration_notice' ); 503 } 504 505 $integration_files = array( 506 'helpers.php', 507 'hero-map.php', 508 'carousel.php', 509 'homepage-settings.php', 510 'homepage.php', 511 'homepage-assets.php', 512 'breadcrumbs.php', 513 'body-classes.php', 514 'plugin-shell.php', 515 'assets.php', 516 ); 517 518 foreach ( $integration_files as $integration_file ) { 519 hvn_realty_load_theme_file( 'inc/integrations/havenlytics/' . $integration_file, true ); 520 } 521 } 522 523 hvn_realty_load_theme_file( 'inc/integration-fallbacks.php', true ); 524 hvn_realty_load_theme_file( 'inc/setup/theme-launch.php', false ); 525 hvn_realty_load_theme_file( 'inc/setup/theme-menus.php', false ); 526 hvn_realty_load_theme_file( 'inc/setup/theme-footer-widgets.php', false ); 527 hvn_realty_load_theme_file( 'inc/elementor.php', true ); 528 hvn_realty_load_theme_file( 'inc/custom-header.php', false ); 529 hvn_realty_load_theme_file( 'inc/template-tags.php', false ); 530 hvn_realty_load_theme_file( 'inc/layout.php', false ); 531 hvn_realty_load_theme_file( 'inc/template-functions.php', false ); 532 hvn_realty_load_theme_file( 'inc/customizer.php', false ); 533 hvn_realty_load_theme_file( 'inc/breadcrumbs.php', false ); 534 hvn_realty_load_theme_file( 'inc/admin/realty-admin.php', true ); 535 hvn_realty_load_theme_file( 'inc/admin/theme-welcome-notice.php', true ); 504 536 505 537 if ( defined( 'JETPACK__VERSION' ) ) { 506 require get_template_directory() . '/inc/jetpack.php';507 } 508 509 require get_template_directory() . '/inc/class-hvn-realty-walker-nav-menu.php';538 hvn_realty_load_theme_file( 'inc/jetpack.php', true ); 539 } 540 541 hvn_realty_load_theme_file( 'inc/class-hvn-realty-walker-nav-menu.php', false ); -
havenlytics-realty/1.15.5/inc/admin/realty-admin.php
r334339 r334420 78 78 function hvn_realty_get_getting_started_url() { 79 79 return hvn_realty_get_realty_admin_url(); 80 }81 82 /**83 * WordPress.org plugin install URL.84 *85 * @return string86 */87 function hvn_realty_get_plugin_install_url() {88 if ( hvn_realty_is_havenlytics_plugin_active() ) {89 return admin_url( 'plugins.php' );90 }91 92 return admin_url( 'plugin-install.php?s=havenlytics&tab=search&type=term' );93 80 } 94 81 … … 272 259 } 273 260 274 wp_enqueue_style( 275 'hvn-realty-admin', 276 HVN_REALTY_TEMPLATE_URL . '/assets/css/admin-realty.css', 277 array(), 278 HVN_REALTY_VERSION 279 ); 261 if ( function_exists( 'hvn_realty_enqueue_theme_style' ) ) { 262 hvn_realty_enqueue_theme_style( 'hvn-realty-admin', 'assets/css/admin-realty.css' ); 263 } elseif ( file_exists( get_template_directory() . '/assets/css/admin-realty.css' ) ) { 264 wp_enqueue_style( 265 'hvn-realty-admin', 266 HVN_REALTY_TEMPLATE_URL . '/assets/css/admin-realty.css', 267 array(), 268 HVN_REALTY_VERSION 269 ); 270 } 280 271 } 281 272 add_action( 'admin_enqueue_scripts', 'hvn_realty_admin_assets' ); -
havenlytics-realty/1.15.5/inc/custom-header.php
r334339 r334420 1 1 <?php 2 2 /** 3 * Disable header/background image features (solid colors only). 3 * Core custom header and background support. 4 * 5 * Site branding uses custom-logo; global background color uses Havenlytics 6 * Theme Settings. Core header/background image panels stay hidden in 7 * inc/customizer/panel-integration.php. 4 8 * 5 9 * @package Havenlytics_Realty … … 7 11 8 12 /** 9 * Disable WordPress core custom header mediasupport.13 * Register WordPress custom header and background support. 10 14 */ 11 function hvn_realty_disable_header_media() { 12 remove_theme_support( 'custom-header' ); 15 function hvn_realty_custom_header_setup() { 16 add_theme_support( 17 'custom-header', 18 apply_filters( 19 'hvn_realty_custom_header_args', 20 array( 21 'default-image' => '', 22 'default-text-color' => '1e1e2f', 23 'width' => 1920, 24 'height' => 400, 25 'flex-height' => true, 26 'flex-width' => true, 27 'uploads' => true, 28 'header-text' => false, 29 'wp-head-callback' => 'hvn_realty_custom_header_style', 30 ) 31 ) 32 ); 33 34 add_theme_support( 35 'custom-background', 36 apply_filters( 37 'hvn_realty_custom_background_args', 38 array( 39 'default-color' => 'f8f8f8', 40 'default-image' => '', 41 ) 42 ) 43 ); 13 44 } 14 add_action( 'after_setup_theme', 'hvn_realty_ disable_header_media', 20);45 add_action( 'after_setup_theme', 'hvn_realty_custom_header_setup' ); 15 46 16 47 /** 17 * Disable WordPress custom background image (keep color via Global Design).48 * Output styles when a core custom header image is set. 18 49 */ 19 function hvn_realty_disable_background_image() { 20 remove_theme_support( 'custom-background' ); 50 function hvn_realty_custom_header_style() { 51 $header_image = get_header_image(); 52 53 if ( empty( $header_image ) ) { 54 return; 55 } 56 ?> 57 <style id="hvn-realty-custom-header-styles" type="text/css"> 58 .hvn-theme-header { 59 background-image: url(<?php echo esc_url( $header_image ); ?>); 60 background-size: cover; 61 background-position: center center; 62 } 63 </style> 64 <?php 21 65 } 22 add_action( 'after_setup_theme', 'hvn_realty_disable_background_image', 20 ); -
havenlytics-realty/1.15.5/inc/customizer.php
r334339 r334420 45 45 array( 46 46 'selector' => '.hvn-theme-logo', 47 'container_inclusive' => true, 48 'render_callback' => 'hvn_realty_customize_partial_logo', 49 ) 50 ); 51 $wp_customize->selective_refresh->add_partial( 52 'custom_logo_mobile_branding', 53 array( 54 'settings' => array( 'custom_logo' ), 55 'selector' => '.hvn-theme-mobile-menu-branding', 47 56 'container_inclusive' => true, 48 57 'render_callback' => 'hvn_realty_customize_partial_logo', … … 170 179 */ 171 180 function hvn_realty_customize_preview_js() { 181 if ( ! file_exists( get_template_directory() . '/assets/js/customizer.js' ) ) { 182 return; 183 } 184 172 185 wp_enqueue_script( 173 186 'hvn-realty-customizer', … … 181 194 $page_for_posts = (int) get_option( 'page_for_posts' ); 182 195 if ( $page_for_posts ) { 183 $blog_url = get_permalink( $page_for_posts ); 196 $permalink = get_permalink( $page_for_posts ); 197 if ( is_string( $permalink ) && $permalink !== '' ) { 198 $blog_url = $permalink; 199 } 184 200 } 185 201 186 202 $localize = array( 187 'fontStacks' => hvn_realty_get_font_stack_map(),188 'googleFonts' => hvn_realty_get_google_fonts_api_map(),203 'fontStacks' => function_exists( 'hvn_realty_get_font_stack_map' ) ? hvn_realty_get_font_stack_map() : array(), 204 'googleFonts' => function_exists( 'hvn_realty_get_google_fonts_api_map' ) ? hvn_realty_get_google_fonts_api_map() : array(), 189 205 'defaultCopyright' => sprintf( 190 206 /* translators: 1: Year, 2: Site name */ … … 196 212 ); 197 213 214 if ( function_exists( 'hvn_realty_get_property_search_url' ) ) { 215 $localize['propertySearchUrl'] = esc_url( hvn_realty_get_property_search_url() ); 216 } 217 218 $localize['homeUrl'] = esc_url( home_url( '/' ) ); 219 198 220 if ( function_exists( 'hvn_realty_get_customizer_home_section_selectors' ) ) { 199 221 $localize['homeSections'] = hvn_realty_get_customizer_home_section_selectors(); -
havenlytics-realty/1.15.5/inc/customizer/controls.php
r334339 r334420 55 55 */ 56 56 function hvn_realty_customize_controls_js() { 57 if ( ! file_exists( get_template_directory() . '/assets/js/customizer-controls.js' ) ) { 58 return; 59 } 60 57 61 wp_enqueue_script( 58 62 'hvn-realty-customizer-controls', -
havenlytics-realty/1.15.5/inc/customizer/helpers.php
r334339 r334420 300 300 301 301 /** 302 * Sanitize a full URL, site path, or page slug for theme links. 303 * 304 * @param string $input Input. 305 * @return string 306 */ 307 function hvn_realty_sanitize_url_or_path( $input ) { 308 $input = trim( (string) $input ); 309 310 if ( '' === $input ) { 311 return ''; 312 } 313 314 if ( preg_match( '#^https?://#i', $input ) ) { 315 return esc_url_raw( $input ); 316 } 317 318 if ( 0 === strpos( $input, '/' ) ) { 319 return '/' . ltrim( sanitize_text_field( wp_unslash( $input ) ), '/' ); 320 } 321 322 return sanitize_title( $input ); 323 } 324 325 /** 326 * Resolve a Customizer link value to a front-end URL. 327 * 328 * @param string $value URL, path, slug, or empty. 329 * @param string $fallback_url Used when value is empty. 330 * @return string 331 */ 332 function hvn_realty_resolve_theme_link( $value, $fallback_url ) { 333 $value = trim( (string) $value ); 334 335 if ( '' === $value ) { 336 return esc_url( $fallback_url ); 337 } 338 339 if ( preg_match( '#^https?://#i', $value ) ) { 340 return esc_url( $value ); 341 } 342 343 if ( 0 === strpos( $value, '/' ) ) { 344 return esc_url( home_url( $value ) ); 345 } 346 347 return esc_url( home_url( '/' . trim( $value, '/' ) . '/' ) ); 348 } 349 350 /** 302 351 * Get heading scale multiplier. 303 352 * -
havenlytics-realty/1.15.5/inc/customizer/homepage-css-output.php
r334339 r334420 144 144 145 145 /** 146 * Stylesheet handle for homepage Customizer inline CSS. 147 * 148 * @return string 149 */ 150 function hvn_realty_get_homepage_inline_style_handle() { 151 if ( wp_style_is( 'hvn-realty-havenlytics-compat', 'registered' ) ) { 152 return 'hvn-realty-havenlytics-compat'; 153 } 154 155 if ( wp_style_is( 'hvn-realty-home-blog', 'registered' ) ) { 156 return 'hvn-realty-home-blog'; 157 } 158 159 return 'hvn-realty-theme'; 160 } 161 162 163 164 /** 146 165 147 166 * Output homepage Customizer styles on the front end. … … 177 196 } 178 197 179 180 181 wp_add_inline_style( 'hvn-realty-home-sections', $css ); 198 $handle = hvn_realty_get_homepage_inline_style_handle(); 199 200 if ( ! wp_style_is( $handle, 'enqueued' ) ) { 201 wp_enqueue_style( $handle ); 202 } 203 204 wp_add_inline_style( $handle, $css ); 182 205 183 206 } -
havenlytics-realty/1.15.5/inc/customizer/loader.php
r334339 r334420 6 6 */ 7 7 8 require get_template_directory() . '/inc/customizer/helpers.php'; 9 require get_template_directory() . '/inc/customizer/controls.php'; 10 require get_template_directory() . '/inc/customizer/sections.php'; 11 require get_template_directory() . '/inc/customizer/sections-homepage.php'; 12 require get_template_directory() . '/inc/customizer/panel-integration.php'; 13 require get_template_directory() . '/inc/customizer/selective-refresh.php'; 14 require get_template_directory() . '/inc/customizer/css-output.php'; 15 require get_template_directory() . '/inc/customizer/homepage-css-output.php'; 8 if ( ! defined( 'ABSPATH' ) ) { 9 exit; 10 } 11 12 if ( ! function_exists( 'hvn_realty_load_theme_file' ) ) { 13 $theme_loader = get_template_directory() . '/inc/theme-loader.php'; 14 if ( file_exists( $theme_loader ) ) { 15 require_once $theme_loader; 16 } 17 } 18 19 $customizer_files = array( 20 'helpers.php', 21 'controls.php', 22 'sections.php', 23 'sections-homepage.php', 24 'panel-integration.php', 25 'selective-refresh.php', 26 'css-output.php', 27 'homepage-css-output.php', 28 ); 29 30 foreach ( $customizer_files as $customizer_file ) { 31 hvn_realty_load_theme_file( 'inc/customizer/' . $customizer_file, false ); 32 } -
havenlytics-realty/1.15.5/inc/customizer/sections-homepage.php
r334339 r334420 594 594 ) 595 595 ); 596 597 hvn_realty_customizer_add_text( 598 $wp_customize, 599 'hvn_realty_home_department_button_text', 600 $section, 601 esc_html__( 'Footer button text', 'havenlytics-realty' ), 602 __( 'View All Properties', 'havenlytics-realty' ) 603 ); 604 605 $wp_customize->add_setting( 606 'hvn_realty_home_department_button_url', 607 array( 608 'default' => '', 609 'sanitize_callback' => 'hvn_realty_sanitize_url_or_path', 610 'transport' => 'postMessage', 611 ) 612 ); 613 $wp_customize->add_control( 614 'hvn_realty_home_department_button_url', 615 array( 616 'label' => esc_html__( 'Footer button link', 'havenlytics-realty' ), 617 'description' => esc_html__( 'Full URL, path (e.g. /properties/), or page slug. Leave empty for the property search page.', 'havenlytics-realty' ), 618 'section' => $section, 619 'type' => 'text', 620 ) 621 ); 596 622 } 597 623 -
havenlytics-realty/1.15.5/inc/customizer/selective-refresh.php
r334339 r334420 41 41 'container_inclusive' => false, 42 42 'render_callback' => function () { 43 echo esc_html( hvn_realty_get_home_cta_headline() ); 43 if ( function_exists( 'hvn_realty_get_home_cta_headline' ) ) { 44 echo esc_html( hvn_realty_get_home_cta_headline() ); 45 } 44 46 }, 45 47 ) … … 52 54 'container_inclusive' => false, 53 55 'render_callback' => function () { 54 echo esc_html( hvn_realty_get_home_cta_subtext() ); 56 if ( function_exists( 'hvn_realty_get_home_cta_subtext' ) ) { 57 echo esc_html( hvn_realty_get_home_cta_subtext() ); 58 } 55 59 }, 56 60 ) … … 63 67 'container_inclusive' => false, 64 68 'render_callback' => function () { 65 echo esc_html( hvn_realty_get_home_cta_primary_text() ); 69 if ( function_exists( 'hvn_realty_get_home_cta_primary_text' ) ) { 70 echo esc_html( hvn_realty_get_home_cta_primary_text() ); 71 } 66 72 }, 67 73 ) … … 75 81 */ 76 82 function hvn_realty_customize_partial_copyright() { 83 if ( ! function_exists( 'hvn_realty_get_copyright_text' ) ) { 84 return; 85 } 86 77 87 echo hvn_realty_get_copyright_text(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 78 88 } … … 82 92 */ 83 93 function hvn_realty_customize_partial_header_cta() { 84 if ( ! hvn_realty_show_header_cta() ) { 94 if ( ! function_exists( 'hvn_realty_show_header_cta' ) || ! hvn_realty_show_header_cta() ) { 95 return; 96 } 97 98 if ( ! function_exists( 'hvn_realty_get_header_cta_text' ) || ! function_exists( 'hvn_realty_get_header_cta_url' ) ) { 85 99 return; 86 100 } -
havenlytics-realty/1.15.5/inc/layout.php
r334339 r334420 67 67 68 68 return is_wp_error( $terms ) ? array() : $terms; 69 } 70 71 /** 72 * Bedroom/bathroom option list for header property search. 73 * 74 * @return array<string, string> Value => label. 75 */ 76 function hvn_realty_get_search_count_select_options() { 77 $options = array( 78 '' => __( 'Any', 'havenlytics-realty' ), 79 ); 80 81 for ( $i = 1; $i <= 5; $i++ ) { 82 $options[ (string) $i ] = (string) $i; 83 } 84 85 $options['6'] = '6+'; 86 87 return $options; 69 88 } 70 89 -
havenlytics-realty/1.15.5/inc/plugin-hooks.php
r334339 r334420 21 21 */ 22 22 function hvn_realty_is_property_context() { 23 return is_singular( 'hvnly_property' ) || is_post_type_archive( 'hvnly_property');23 return post_type_exists( 'hvnly_property' ) && ( is_singular( 'hvnly_property' ) || is_post_type_archive( 'hvnly_property' ) ); 24 24 } 25 25 … … 58 58 59 59 /** 60 * WordPress.org plugin install URL (or plugins screen when already active). 61 * 62 * @return string 63 */ 64 if ( ! function_exists( 'hvn_realty_get_plugin_install_url' ) ) { 65 function hvn_realty_get_plugin_install_url() { 66 if ( hvn_realty_is_havenlytics_plugin_active() ) { 67 return admin_url( 'plugins.php' ); 68 } 69 70 return admin_url( 'plugin-install.php?s=havenlytics&tab=search&type=term' ); 71 } 72 } 73 74 /** 60 75 * Register integration hooks when plugin is present. 61 76 */ … … 79 94 */ 80 95 function hvn_realty_property_archive_classes( $classes ) { 81 if ( hvn_realty_is_property_view() ) {96 if ( function_exists( 'hvn_realty_is_property_view' ) && hvn_realty_is_property_view() ) { 82 97 $classes[] = 'hvn-property-view'; 83 98 $classes = apply_filters( 'hvn_realty_property_layout_classes', $classes ); … … 91 106 */ 92 107 function hvn_realty_property_styles_hook() { 93 if ( ! hvn_realty_is_property_view() ) {108 if ( ! function_exists( 'hvn_realty_is_property_view' ) || ! hvn_realty_is_property_view() ) { 94 109 return; 95 110 } … … 126 141 */ 127 142 function hvn_realty_property_archive_before() { 128 if ( ! is_post_type_archive( 'hvnly_property' ) ) {143 if ( ! post_type_exists( 'hvnly_property' ) || ! is_post_type_archive( 'hvnly_property' ) ) { 129 144 return; 130 145 } … … 136 151 */ 137 152 function hvn_realty_property_single_after() { 138 if ( ! is_singular( 'hvnly_property' ) ) {153 if ( ! post_type_exists( 'hvnly_property' ) || ! is_singular( 'hvnly_property' ) ) { 139 154 return; 140 155 } -
havenlytics-realty/1.15.5/inc/template-functions.php
r334339 r334420 77 77 $classes[] = 'hvn-container-' . hvn_realty_get_container_mode(); 78 78 79 if ( ! hvn_realty_show_header_search() ) {79 if ( ! function_exists( 'hvn_realty_show_header_search' ) || ! hvn_realty_show_header_search() ) { 80 80 $classes[] = 'hvn-header-no-search'; 81 81 } 82 82 83 if ( ! hvn_realty_show_header_cta() ) {83 if ( ! function_exists( 'hvn_realty_show_header_cta' ) || ! hvn_realty_show_header_cta() ) { 84 84 $classes[] = 'hvn-header-no-cta'; 85 85 } -
havenlytics-realty/1.15.5/readme.txt
r334339 r334420 4 4 Tags: two-columns, right-sidebar, custom-logo, custom-menu, featured-images, full-width-template, theme-options, translation-ready, block-styles, wide-blocks 5 5 Requires at least: 6.0 6 Tested up to: 6.96 Tested up to: 7.0 7 7 Requires PHP: 7.4 8 Stable tag: 1.15. 08 Stable tag: 1.15.5 9 9 License: GPLv2 or later 10 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html 11 11 12 Havenlytics Realty is the official companion theme for the free Havenlytics plugin — launch a professional real estate website with search, listings, and agents in minutes.12 Official companion theme for the free Havenlytics plugin — launch a real estate website with map search, listings, agents, and agencies. 13 13 14 14 == Description == 15 15 16 **Havenlytics Realty is the official companion theme for the free [Havenlytics](https://wordpress.org/plugins/havenlytics/) plugin.** Install both to launch a property directory with search, listings, agents, and agencies — or use the theme on its own as a fast, accessible blog and business layout. 17 18 The theme automatically enhances the Havenlytics experience when both are active: a 10-section real estate homepage, plugin page styling, theme breadcrumbs, a Realty setup hub, and Customizer controls for hero text, listing counts, and section visibility. 19 20 = Launch in 4 steps = 21 22 1. Activate **Havenlytics Realty** 23 2. Go to **Appearance → Realty** 24 3. Install and activate the **Havenlytics** plugin (recommended, not required) 25 4. Run the plugin **Setup Wizard** to import demo listings — the theme configures your homepage and menu automatically 26 27 = Real estate homepage (with Havenlytics plugin) = 28 29 * Hero property search section 30 * Featured and latest property listings 31 * Property category cards (Sale, Rent, and more) 32 * Featured agents and agencies 33 * Live statistics (properties, agents, agencies) 34 * Call-to-action banners and blog section 35 * Full Customizer control over hero, counts, and section toggles 16 **[Live demo](https://demo.havenlytics.com/)** | **[Havenlytics plugin](https://wordpress.org/plugins/havenlytics/)** | **[Theme author](https://profiles.wordpress.org/havenlytics/)** 17 18 Havenlytics Realty is the official WordPress theme built to work with the free **[Havenlytics](https://wordpress.org/plugins/havenlytics/)** property plugin. Together they deliver a complete real estate website: interactive map search, filtered property archives, agent and agency directories, and a conversion-focused homepage — without custom code. 19 20 Use the theme on its own for a fast, accessible blog or business site. Activate Havenlytics to unlock the full real estate experience. 21 22 = What Havenlytics Realty provides = 23 24 * A purpose-built **real estate homepage** with eight customizable sections 25 * **Appearance → Realty** onboarding hub with setup checklist and site snapshot 26 * **Automatic site launch** after demo import (homepage, menus, front page) 27 * **CSS harmonization** for property, agent, and agency pages 28 * **Header property search panel** with keyword, type, location, price, and bed/bath filters 29 * **Theme breadcrumbs** on listings, taxonomies, and directory pages 30 * **Customizer controls** for homepage sections, hero map height, listing counts, and CTAs 31 * **Full-width layouts** for plugin shortcode pages 32 * Accessible, responsive foundation for blogs and business pages 33 34 = What the Havenlytics plugin adds = 35 36 The plugin is the property engine. It registers properties, agents, and agencies; powers AJAX search with grid, list, and map views; renders single property pages with galleries, maps, and Contact Agent forms; and includes a Setup Wizard to import demo listings. 37 38 **Install both** to get the experience shown in the [live demo](https://demo.havenlytics.com/). 39 40 = Real estate homepage (Havenlytics plugin active) = 41 42 Eight sections, each toggleable in **Appearance → Customize → Real Estate Homepage**: 43 44 1. **Hero map** — Interactive property map with Customizer department filtering and adjustable height (desktop/mobile) 45 2. **Featured properties** — Curated listing carousel (featured meta) 46 3. **Properties by department** — Tabbed grids per department (Sale, Rent, Let, Commercial, etc.) 47 4. **Properties by location** — Location cards with term images and listing counts 48 5. **Agents** — Agent directory carousel 49 6. **Agencies** — Agency directory carousel 50 7. **Latest blog posts** — Content marketing section (core WordPress posts) 51 8. **Call to action** — Search and agent discovery prompts 52 53 **Header search:** The search icon opens a property search panel (keyword, type, location, bedrooms, bathrooms, min/max price) that submits to the plugin Property Search page. 36 54 37 55 = Havenlytics plugin integration = 38 56 39 * CSS harmonization for property, agent, and agency pages 40 * Header search links to the plugin Property Search page 41 * Theme breadcrumbs on listings and agent archives 42 * Full-width layout for plugin shortcode pages 43 * Works with plugin templates — no forked overrides required 57 * Detects the plugin automatically — no theme edits to plugin files 58 * Primes plugin assets on the homepage for shortcode-rendered sections 59 * Resolves plugin page URLs for search, grid, lists, agents, and agencies 60 * Applies Customizer design tokens (primary, secondary, text) to plugin UI 61 * Adds context body classes for property, agent, agency, and shortcode pages 62 * Hooks `hvnly_before_main_content` for breadcrumb alignment 63 * Hides duplicate breadcrumbs on the real estate homepage 64 * Friendly fallback prompt when the plugin is not installed 44 65 45 66 = Theme foundation = … … 64 85 = Performance = 65 86 66 * Minimal vanilla JavaScript (no jQuery on the front end)87 * Minimal vanilla JavaScript on the front end (jQuery only where plugin/home scripts require it) 67 88 * Conditional stylesheet loading per template 68 89 * Google Fonts loaded only for selected typography choices … … 70 91 = Works without the plugin = 71 92 72 Havenlytics Realty remains a fully functional WordPress theme for blogs, business pages, and directories. Without the Havenlytics plugin, the real estate homepage shows a friendlyprompt to install the plugin — no errors, no broken layouts.93 Havenlytics Realty remains a fully functional WordPress theme for blogs, business pages, and directories. Without Havenlytics, the real estate homepage shows a clear prompt to install the plugin — no errors, no broken layouts. 73 94 74 95 == Installation == … … 76 97 = Quick start (recommended) = 77 98 78 1. Install and activate **Havenlytics Realty** from Appearance → Themes.99 1. Install and activate **Havenlytics Realty** from **Appearance → Themes**. 79 100 2. Open **Appearance → Realty** for the setup checklist. 80 3. Install the free **[Havenlytics plugin](https://wordpress.org/plugins/havenlytics/)** when prompted (recommended, not required). 81 4. Run the plugin **Setup Wizard** to import demo properties. 82 5. Visit your site — the theme creates the homepage, primary menu, and front page automatically. 101 3. Install and activate the free **[Havenlytics plugin](https://wordpress.org/plugins/havenlytics/)** when prompted. 102 4. In the plugin admin, run the **Property Import / Setup Wizard** to import demo listings, agents, and agencies. 103 5. Visit your site — the theme automatically configures the homepage, primary menu, and static front page after import. 104 105 **See the finished result:** [https://demo.havenlytics.com/](https://demo.havenlytics.com/) 83 106 84 107 = Manual install = 85 108 86 1. In your admin panel, go to Appearance → Themesand click **Add New**.109 1. In your admin panel, go to **Appearance → Themes** and click **Add New**. 87 110 2. Search for **Havenlytics Realty** or upload the theme ZIP file. 88 111 3. Click **Activate**. … … 91 114 = After activation = 92 115 93 * **Appearance → Realty** — onboarding checklist and quick links94 * **Appearance → Customize → Real Estate Homepage** — hero , listings, and section controls (requires Havenlytics plugin)116 * **Appearance → Realty** — onboarding checklist, progress ring, and quick links 117 * **Appearance → Customize → Real Estate Homepage** — hero map, sections, counts, and CTAs (requires Havenlytics plugin) 95 118 * **Appearance → Customize → Havenlytics Theme Settings** — global design, header, footer, typography, layout 96 119 120 == Plugin integration == 121 122 Havenlytics Realty is designed as the **official front-end companion** for the Havenlytics plugin. Integration is entirely theme-side. 123 124 = Plugin features represented in the theme = 125 126 | Plugin capability | How the theme showcases it | 127 |-------------------|----------------------------| 128 | Property map search | Hero map section; header search panel; links to Property Search page | 129 | Property grid | Department tabs (`[hvnly_property_grid]`); Featured carousel (theme query) | 130 | Departments (`hvnly_prop_depts`) | Department tabs; hero map department filter; primary menu | 131 | Locations (`hvnly_prop_locations`) | Location cards; header search location filter | 132 | Property types (`hvnly_prop_types`) | Header search type filter; featured card badges | 133 | Featured listings | Featured properties homepage section | 134 | Agents (`hvnly_agent`) | Agents carousel; CTA links; breadcrumbs; CSS harmonization | 135 | Agencies (`hvnly_agent_agency`) | Agencies carousel; breadcrumbs; CSS harmonization | 136 | Property singles | Styled via compat CSS; theme breadcrumbs; full-width layout | 137 | Property archives & taxonomies | Breadcrumbs; body classes; archive styling | 138 | Contact Agent | Available on plugin single property pages (plugin feature; theme styles the page) | 139 | Setup Wizard demo import | Triggers theme auto-launch (homepage, menus, front page) | 140 | Five plugin pages | Auto-linked in primary menu and footer widgets after launch | 141 142 = Plugin pages created on install = 143 144 * Property Search (`[hvnly_property_search]`) — full filters, grid/list/map views 145 * Property Grid (`[hvnly_property_grid]`) 146 * Property Lists (`[hvnly_property_lists]`) — list layout 147 * Property Agents (`[hvnly_property_agents]`) 148 * Property Agencies (`[hvnly_property_agencies]`) 149 150 The theme links to these pages from the homepage, header search, CTAs, and menus. Property Search and Lists pages are not embedded as homepage sections — users navigate to them via search and menu links. 151 152 = What stays in the plugin = 153 154 Property Builder layouts, archive filter sidebar configuration, Contact Agent forms, AJAX search logic, map providers, import wizard, and single-property widgets remain plugin responsibilities. The theme does not modify plugin code. 155 97 156 == Frequently Asked Questions == 98 157 99 158 = Do I need the Havenlytics plugin? = 100 159 101 No. Havenlytics Realty works as a standalone WordPress theme for blogs and business sites. For property listings, search, agents, agencies, and the real estate homepage, install the free [Havenlytics plugin](https://wordpress.org/plugins/havenlytics/) — the theme is built as its official companion. 160 No. Havenlytics Realty works as a standalone WordPress theme. For property listings, search, agents, agencies, and the real estate homepage, install the free [Havenlytics plugin](https://wordpress.org/plugins/havenlytics/) — the theme is built as its official companion. 161 162 = Where is the live demo? = 163 164 [https://demo.havenlytics.com/](https://demo.havenlytics.com/) — Havenlytics Realty with the Havenlytics plugin and demo content. 102 165 103 166 = How do I launch a real estate homepage? = … … 109 172 No. All integration is theme-side: shortcodes, CSS harmonization, body classes, breadcrumbs, and admin onboarding. The plugin continues to work with any WordPress theme. 110 173 111 = Does this theme support plugins? = 112 113 Yes. Havenlytics Realty integrates with the Havenlytics property plugin, Contact Form 7, Jetpack, and Elementor. 174 = How do I search for properties from the homepage? = 175 176 Click the **search icon** in the header to open the property search panel, or use the **Call to Action** section and menu links to the Property Search page. 177 178 = Does this theme support Elementor? = 179 180 Yes. Havenlytics Realty supports Elementor full-width layouts. The Havenlytics plugin also provides Elementor widgets for property archives, agents, and agencies. 114 181 115 182 = Is this theme accessibility ready? = … … 119 186 = How do I change the primary color? = 120 187 121 Go to **Appearance → Customize → Havenlytics Theme Settings → Global Design System**. 188 Go to **Appearance → Customize → Havenlytics Theme Settings → Global Design System**. Colors also apply to Havenlytics plugin pages when both are active. 122 189 123 190 = How do I customize the real estate homepage? = 124 191 125 With the Havenlytics plugin active, go to **Appearance → Customize → Real Estate Homepage** to edit the hero, listing counts, CTA text, and section visibility. 126 127 = How do I change blog columns or posts per page? = 128 129 Go to **Appearance → Customize → Havenlytics Theme Settings → Layout Settings**. 192 With the Havenlytics plugin active, go to **Appearance → Customize → Real Estate Homepage** to edit section visibility, hero map height, listing counts, department filters, CTA text, and more. 130 193 131 194 == Screenshots == 132 195 133 1. Real estate homepage with hero search and featured property listings (Havenlytics plugin active) 134 2. Property single page with theme header and styled listing details 135 3. Property search page with filters and results grid 136 4. Agents directory grid page 137 5. Customizer — Real Estate Homepage settings (hero, counts, section toggles) 138 6. Mobile homepage with responsive search and listing cards 196 Recommended WordPress.org screenshot set (accurate to the product): 197 198 1. **Homepage** — Hero map, featured properties, and department tabs ([live demo](https://demo.havenlytics.com/)) 199 2. **Header search panel** — Expanded property search modal with filters 200 3. **Property single** — Gallery, details, and Contact Agent (plugin) with theme header 201 4. **Property search page** — Grid/list/map views and filter sidebar (plugin page) 202 5. **Agents directory** — Agent cards grid (plugin page or homepage section) 203 6. **Customizer** — Real Estate Homepage panel with section controls 204 7. **Appearance → Realty** — Onboarding hub with setup checklist 205 8. **Mobile homepage** — Responsive map hero and listing cards 139 206 140 207 == Changelog == 208 209 = 1.15.5 - June 8, 2026 = 210 * Premium desktop dropdown styling: soft fade + translateY reveal, elevated panel shadow, improved spacing and typography 211 * Removed submenu label shift on hover (no padding-left jump); stable hover and focus highlights 212 * Fixed mobile dropdown arrow anchoring so the toggle stays fixed while submenus expand underneath 213 * Mobile submenu open/close now uses max-height and opacity (no display toggle or sliding keyframes) 214 * Submenu state resets when the mobile menu closes 215 216 = 1.15.4 - June 8, 2026 = 217 * Fixed desktop dropdown menus closing when moving the pointer from parent item to submenu 218 * Added hover bridge and open-state handling for more reliable desktop submenu targeting 219 * Improved desktop dropdown transitions, submenu link hit area, and aria-expanded keyboard support 220 * Improved mobile submenu toggle indicators, aria-controls wiring, and expand/collapse behavior 221 * Navigation accessibility refinements for focus, Escape, and screen reader states 222 223 = 1.15.3 - June 8, 2026 = 224 * Synchronized mobile off-canvas menu branding with Appearance → Customize → Site Identity custom logo 225 * Added Customizer selective refresh and live preview support for the mobile menu logo 226 * Improved desktop navigation dropdown transitions, hover states, focus states, and active submenu styles 227 * Improved mobile menu spacing, tap targets, submenu toggle controls, and visual hierarchy 228 * Responsive refinements for header logo sizing, homepage cards, department tabs, and footer columns (320px–1440px) 229 230 = 1.15.2 - June 8, 2026 = 231 * Fixed hero map height Customizer controls (desktop and mobile vh) overriding plugin fixed map heights 232 * Fixed homepage Customizer inline CSS attaching to the correct stylesheet handle 233 * Department section footer button text and URL Customizer controls 234 * Expanded header property search panel with bedrooms, bathrooms, and price filters; smoother modal animation 235 * WordPress.org readme and product description accuracy improvements 141 236 142 237 = 1.15.0 - June 8, 2026 = … … 154 249 * Phase 5: WordPress.org companion positioning — readme, installation guide, FAQ, and screenshot list 155 250 * Updated theme description for Havenlytics plugin pairing 156 * TGMPA recommended-plugin notice with Getting Started guidance157 251 * Font credits updated (Inter, Poppins) 158 252 … … 169 263 * Manual theme setup trigger when demo is imported but homepage not configured 170 264 * Welcome and launch notices link to Getting Started page 171 * Getting Started link added on Themes screen172 265 173 266 = 1.9.0 - June 8, 2026 = … … 180 273 181 274 = 1.8.0 - June 8, 2026 = 182 * Added Real Estate Homepage template with 10 sections (hero search, featured/latest listings, categories, agents, agencies, stats, CTAs, blog)275 * Added Real Estate Homepage template with homepage sections (hero map, featured listings, departments, locations, agents, agencies, blog, CTA) 183 276 * Added theme launch pack: auto-creates Home page, Primary menu, and front page assignment after demo import 184 277 * Added Havenlytics integration module (helpers, shortcode wrappers, compat CSS) … … 187 280 = 1.7.4 - June 8, 2026 = 188 281 * Fixed Havenlytics plugin detection (HvnlyNab class) and hvnly_property CPT integration 189 * Header search iconlinks to the Havenlytics Property Search page when the plugin is active282 * Header search links to the Havenlytics Property Search page when the plugin is active 190 283 * Property archives no longer receive blog grid body classes 191 284 * Added dismissible admin welcome notice after theme activation 192 285 * Added read-only plugin page ID helper for theme integration without modifying plugin code 193 286 194 = 1.7.3 - June 8, 2026 =195 * Fixed blog pagination double-box styling (removed duplicate CSS + inner span wrappers)196 * Pages and singles use full container width by default; sidebar grid only when sidebar is active197 * Removed unnecessary layout-row markup when no sidebar is assigned198 * Added animated hover effects for blog post cards199 * Elementor full-width reset scoped to #primary only — header/footer containers stay boxed200 201 = 1.7.2 - June 8, 2026 =202 * Reordered Customizer: Global Design System, Site Identity, Header, Typography, Layout, Footer203 * Removed header/background image sections and custom-background theme support204 * Fixed Posts Per Row grid on frontend (legacy layout CSS conflict + body/grid class collision)205 * Blog column CSS now matches Customizer preview selectors on saved frontend output206 207 = 1.6.0 - June 8, 2026 =208 * Architecture: Full template rebuild — isolated page, blog, and single layouts (Astra-style)209 * Removed: Search modal overlay (HTML, CSS, JS); header search icon now links to /?s= search page210 * Fixed: Page layout no longer force-centers content at 900px; full container width by default211 * Added: single.css loaded only on single posts; unified optional sidebar module (.hvn-theme-sidebar-area)212 * Fixed: Elementor pages get full-width container reset without breaking standard pages213 * CSS: Strict file split — page.css, blog.css, single.css, layouts.css (shared utilities only)214 215 = 1.5.1 - June 8, 2026 =216 * Refactor: Complete page/blog separation — dedicated `hvn-page-*` markup and `page.css` / `blog.css` loaded per context217 * Added: Conditional stylesheet enqueue (pages + 404 → page.css; blog/archive/search → blog.css)218 * Added: Body class `hvn-view-page` for static pages and 404219 * Fixed: Archive titles use `hvn-blog-title` instead of shared page title class220 * Fixed: 404 template uses page shell markup; inline blog grid CSS attaches to blog handle only221 222 = 1.5.0 - June 8, 2026 =223 * Refactor: Isolated layout system — blog, page, and single post use separate wrappers and CSS224 * Fixed: Removed global 12-column grid leakage from page/single/blog templates225 * Fixed: Sidebar default is now none; only renders when Customizer enables it226 * Added: layouts.css with scoped blog grid, page (900px), single (800px), pagination, breadcrumbs227 * Improved: Breadcrumb SEO schema (JSON-LD BreadcrumbList) and Home > Blog > Category > Post trail228 229 = 1.4.0 - June 8, 2026 =230 * Fixed: Typography system — Inter (body) + Poppins (headings) with optimized Google Fonts loading231 * Fixed: Mobile header navigation (hamburger menu, desktop nav hide/show)232 * Fixed: Header CTA visibility on desktop; CTA added to mobile drawer233 * Fixed: Blog grid cards, single post readability, search page, and default page layouts234 * Fixed: Footer widget columns, spacing, and bottom bar alignment235 * Added: Production polish stylesheet (assets/css/polish.css) for responsive and UI consistency236 237 = 1.3.1 - June 8, 2026 =238 * Added: Core WP Customizer sections (Site Identity, Header Image, Background) moved into Havenlytics Theme Settings panel239 * Added: Block editor design token output matching frontend Customizer values240 * Added: Selective refresh for copyright and header CTA241 * Added: Live preview for typography fonts, blog layout, container mode, and header toggles242 * Added: Property archive/single plugin hook calls in templates243 * Fixed: Footer widget text/title colors respect Customizer footer color settings244 245 = 1.3.0 - June 8, 2026 =246 * Added: Modular Customizer architecture (inc/customizer/)247 * Added: Typography section — font families, line height, heading scale248 * Added: Border color, transparent header, CTA toggle/link, footer link color, back-to-top249 * Added: Container width mode (boxed/full) and plugin-ready hooks (inc/plugin-hooks.php)250 * Changed: Dynamic Google Fonts loading from Customizer typography choices251 252 = 1.2.0 - June 8, 2026 =253 * Added: Unified Customizer panel "Havenlytics Theme Settings" (Global Design, Header, Footer, Layout)254 * Refactor: Modern header markup (hvn-theme-header, hvn-theme-nav, hvn-theme-actions)255 * Refactor: Modern footer markup (hvn-theme-footer, hvn-cols-{n}, hvn-theme-footer-bottom)256 * Added: Header layout options (1–3), search toggle, CTA button text257 * Added: Footer column count, footer background/text colors via CSS variables258 * Changed: CSS consolidated to theme.css and editor.css only; property styles merged into theme.css259 260 = 1.1.0 - June 8, 2026 =261 * Refactor: Merged fragmented CSS into assets/css/theme.css262 * Added: Havenlytics Global Settings customizer section with design tokens263 * Added: Elementor support, theme.json (FSE-ready), property.css placeholder264 * Added: Header/footer template parts; vanilla JS theme.js (no jQuery)265 * Changed: Footer widgets expanded to 4 columns266 * Changed: Sticky header is now optional via customizer267 268 = 1.0.5 - May 26, 2026 =269 * Fixed: CSS small issues with blog columns and mobile menu270 271 = 1.0.4 - May 26, 2026 =272 * Added: Added TGMPA plugin activation for recommended plugins (Havenlytics Core Plugin)273 274 = 1.0.3 - May 15, 2026 =275 * Fixed: Removed prohibited tags (blog, real-estate, property-listings, directory) from style.css header276 * Fixed: Added underline styling for all content links (posts, excerpts, comments, text widgets) to meet WordPress.org requirements277 * Fixed: Removed direct wp_enqueue_script('jquery') and now loading jQuery as dependency only278 * Fixed: Added default header image path in custom-header.php279 * Fixed: Removed missing accessibility.js file reference from functions.php280 * Improved: Code quality and WordPress.org theme review standards compliance281 282 = 1.0.2 =283 * Added: Primary menu fallback when no menu is assigned284 * Added: Mobile menu fallback for better user experience285 * Fixed: Blog columns CSS override issues286 * Improved: Mobile menu keyboard navigation287 * Improved: Accessibility enhancements288 289 = 1.0.1 =290 * Added: Professional breadcrumb navigation with schema markup291 * Added: Custom blog columns setting (1-4 columns)292 * Added: Posts per page control in Customizer293 * Added: Ignore sticky posts option294 * Added: Scroll to top button with animation295 * Added: Customizer layout options section296 * Improved: Blog grid and list layout options297 * Improved: Sticky sidebar functionality298 299 = 1.0.0 =300 * Initial release301 * Full accessibility support302 * Keyboard navigation303 * Focus styles304 * WordPress.org standards compliance305 * Custom block styles and patterns306 * Sticky sidebar307 * Modern pagination design308 * Mobile-responsive hamburger menu309 * Customizable blog columns (1-4 columns)310 * Posts per page control311 * Ignore sticky posts option312 * Professional breadcrumb navigation313 * Scroll to top button with pulse animation314 * Modern widget styling315 * Customizer controls for layout options316 317 287 == Credits == 318 288 … … 330 300 All images used in the theme screenshot and demo content are from free stock photo websites with GPL-compatible licenses. 331 301 332 == Image for theme screenshot ==333 334 License: CC0 1.0 Universal (CC0 1.0)335 License URL: https://pxhere.com/en/license336 Source: https://pxhere.com/en/photo/979977337 338 License: CC0 1.0 Universal (CC0 1.0)339 License URL: https://pxhere.com/en/license340 Source: https://pxhere.com/en/photo/709518341 342 License: CC0 1.0 Universal (CC0 1.0)343 License URL: https://pxhere.com/en/license344 Source: https://pxhere.com/en/photo/15020345 346 License: CC0 1.0 Universal (CC0 1.0)347 License URL: https://pxhere.com/en/license348 Source: https://pxhere.com/en/photo/1284562349 350 License: CC0 1.0 Universal (CC0 1.0)351 License URL: https://pxhere.com/en/license352 Source: https://pxhere.com/en/photo/966576353 354 License: CC0 1.0 Universal (CC0 1.0)355 License URL: https://pxhere.com/en/license356 Source: https://pxhere.com/en/photo/1094638357 358 = SVG Icons =359 360 Custom SVG icons used in the theme (scroll to top, search, menu, breadcrumb home) are original creations by the theme author and are licensed under GPLv2 or later.361 362 302 == Additional Information == 363 303 … … 365 305 Havenlytics Realty Theme is distributed under the terms of the GNU GPL. 366 306 367 * Theme: https://wordpress.org/themes/havenlytics-realty/ 368 * Plugin: https://wordpress.org/plugins/havenlytics/ 369 * Author: https://profiles.wordpress.org/havenlytics/ 307 * **Live demo:** https://demo.havenlytics.com/ 308 * **Theme:** https://wordpress.org/themes/havenlytics-realty/ 309 * **Plugin:** https://wordpress.org/plugins/havenlytics/ 310 * **Author:** https://profiles.wordpress.org/havenlytics/ 370 311 371 312 == Upgrade Notice == 313 314 = 1.15.5 = 315 Premium navigation interaction polish: desktop dropdown animation and styling, fixed mobile arrow positioning, smoother submenu expand. No breaking changes. 316 317 = 1.15.4 = 318 Navigation improvements: reliable desktop dropdowns, mobile submenu usability, and accessibility refinements. No breaking changes. 319 320 = 1.15.3 = 321 Logo synchronization, navigation polish, mobile menu usability, and responsive refinements. No breaking changes. 322 323 = 1.15.2 = 324 Hero map height fix, header search panel improvements, department button Customizer controls, and readme accuracy updates. No breaking changes. 372 325 373 326 = 1.15.0 = … … 376 329 = 1.12.0 = 377 330 WordPress.org companion positioning update. Readme, onboarding guide, and Havenlytics plugin pairing documentation improved. No breaking changes. 378 379 = 1.11.0 =380 Adds Real Estate Homepage Customizer controls when the Havenlytics plugin is active.381 382 = 1.0.3 =383 This release includes important fixes for WordPress.org theme review compliance. Please update to ensure your theme meets all requirements.384 385 = 1.0.0 =386 Initial release. A modern, accessible real estate blog theme with full customizer controls, multiple layout options, and professional breadcrumb navigation. -
havenlytics-realty/1.15.5/style.css
r334339 r334420 4 4 Author: Havenlytics 5 5 Author URI: https://profiles.wordpress.org/havenlytics/ 6 Description: Official companion theme for the free Havenlytics plugin. Launch a real estate website with property search, listings, and agents — or use as a lightweight, accessible blog and business theme. 7 Version: 1.15.0 8 Tested up to: 6.9 6 Description: Official companion theme for the free Havenlytics plugin — build a real estate, realtor, or estate agency website with property listings, map search, agent directories, and agency directories. Live demo: https://demo.havenlytics.com/ 7 Version: 1.15.5 8 Requires at least: 6.0 9 Tested up to: 7.0 9 10 Requires PHP: 7.4 10 11 License: GPLv2 or later … … 59 60 60 61 62 61 63 /* Required WordPress Classes */ 62 64 .wp-caption { … … 258 260 display: inline-block; 259 261 } 262 263 -
havenlytics-realty/1.15.5/template-parts/header/mobile-menu.php
r334339 r334420 8 8 <div class="hvn-theme-mobile-menu" id="hvn-mobile-menu" aria-hidden="true"> 9 9 <div class="hvn-theme-mobile-menu-header"> 10 <div class="hvn-theme-mobile-menu- title">11 <?php echo esc_html( get_bloginfo( 'name' )); ?>10 <div class="hvn-theme-mobile-menu-branding"> 11 <?php get_template_part( 'template-parts/header/branding' ); ?> 12 12 </div> 13 13 <button class="hvn-theme-mobile-menu-close" type="button" aria-label="<?php esc_attr_e( 'Close Menu', 'havenlytics-realty' ); ?>"> -
havenlytics-realty/1.15.5/template-parts/header/navigation.php
r334339 r334420 9 9 <?php 10 10 if ( has_nav_menu( 'primary' ) ) { 11 $walker = function_exists( 'hvn_realty_get_nav_menu_walker' ) ? hvn_realty_get_nav_menu_walker() : null; 12 11 13 wp_nav_menu( 12 14 array( … … 17 19 'fallback_cb' => '__return_false', 18 20 'depth' => 3, 19 'walker' => new HVN_Realty_Walker_Nav_Menu(),21 'walker' => $walker, 20 22 ) 21 23 ); -
havenlytics-realty/1.15.5/template-parts/header/property-search-panel.php
r334339 r334420 1 1 <?php 2 2 3 /** 4 3 5 * Header property search panel (overlay form). 6 4 7 * 8 5 9 * @package Havenlytics_Realty 10 6 11 */ 7 12 13 14 8 15 if ( ! defined( 'ABSPATH' ) ) { 16 9 17 exit; 18 10 19 } 11 20 21 22 12 23 if ( ! function_exists( 'hvn_realty_use_header_property_search_panel' ) || ! hvn_realty_use_header_property_search_panel() ) { 24 13 25 return; 26 14 27 } 15 28 29 30 16 31 $search_url = hvn_realty_get_property_search_url(); 32 17 33 $property_types = hvn_realty_get_header_search_terms( 'hvnly_prop_types' ); 34 18 35 $locations = hvn_realty_get_header_search_terms( 'hvnly_prop_locations' ); 36 37 $count_options = hvn_realty_get_search_count_select_options(); 38 19 39 ?> 40 20 41 <div id="hvn-property-search-panel" class="hvn-theme-property-search-panel" aria-hidden="true" hidden> 42 21 43 <div class="hvn-theme-property-search-panel__overlay" data-hvn-search-close tabindex="-1" aria-hidden="true"></div> 44 22 45 <div class="hvn-theme-property-search-panel__dialog" role="dialog" aria-modal="true" aria-labelledby="hvn-property-search-panel-title"> 46 23 47 <button type="button" class="hvn-theme-property-search-panel__close" data-hvn-search-close aria-label="<?php esc_attr_e( 'Close search', 'havenlytics-realty' ); ?>"> 48 24 49 <span aria-hidden="true">×</span> 50 25 51 </button> 26 52 53 54 27 55 <div class="hvn-theme-property-search-panel__inner"> 56 28 57 <p class="hvn-theme-property-search-panel__eyebrow"><?php esc_html_e( 'Property search', 'havenlytics-realty' ); ?></p> 58 29 59 <h2 id="hvn-property-search-panel-title" class="hvn-theme-property-search-panel__title"> 60 30 61 <?php esc_html_e( 'Find your next property', 'havenlytics-realty' ); ?> 62 31 63 </h2> 32 64 65 66 33 67 <form class="hvn-theme-property-search-panel__form" method="get" action="<?php echo esc_url( $search_url ); ?>"> 68 34 69 <div class="hvn-theme-property-search-panel__field"> 70 35 71 <label for="hvn-header-address-keyword"><?php esc_html_e( 'Keyword or address', 'havenlytics-realty' ); ?></label> 72 36 73 <input 74 37 75 type="search" 76 38 77 class="hvn-theme-form-control" 78 39 79 id="hvn-header-address-keyword" 80 40 81 name="address_keyword" 82 41 83 autocomplete="off" 84 42 85 placeholder="<?php esc_attr_e( 'City, neighborhood, or address', 'havenlytics-realty' ); ?>" 86 43 87 /> 44 </div> 88 89 </div> 90 91 45 92 46 93 <div class="hvn-theme-property-search-panel__row"> 47 <div class="hvn-theme-property-search-panel__field"> 94 95 <div class="hvn-theme-property-search-panel__field"> 96 48 97 <label for="hvn-header-property-type"><?php esc_html_e( 'Property type', 'havenlytics-realty' ); ?></label> 98 49 99 <select class="hvn-theme-form-control" id="hvn-header-property-type" name="property_type"> 100 50 101 <option value=""><?php esc_html_e( 'Any type', 'havenlytics-realty' ); ?></option> 102 51 103 <?php foreach ( $property_types as $term ) : ?> 104 52 105 <option value="<?php echo esc_attr( $term->slug ); ?>"><?php echo esc_html( $term->name ); ?></option> 53 <?php endforeach; ?> 54 </select> 55 </div> 56 57 <div class="hvn-theme-property-search-panel__field"> 106 107 <?php endforeach; ?> 108 109 </select> 110 111 </div> 112 113 114 115 <div class="hvn-theme-property-search-panel__field"> 116 58 117 <label for="hvn-header-location"><?php esc_html_e( 'Location', 'havenlytics-realty' ); ?></label> 118 59 119 <select class="hvn-theme-form-control" id="hvn-header-location" name="location"> 120 60 121 <option value=""><?php esc_html_e( 'Any location', 'havenlytics-realty' ); ?></option> 122 61 123 <?php foreach ( $locations as $term ) : ?> 124 62 125 <option value="<?php echo esc_attr( $term->slug ); ?>"><?php echo esc_html( $term->name ); ?></option> 63 <?php endforeach; ?> 64 </select> 65 </div> 66 </div> 126 127 <?php endforeach; ?> 128 129 </select> 130 131 </div> 132 133 </div> 134 135 136 137 <div class="hvn-theme-property-search-panel__row"> 138 139 <div class="hvn-theme-property-search-panel__field"> 140 141 <label for="hvn-header-bedrooms"><?php esc_html_e( 'Bedrooms', 'havenlytics-realty' ); ?></label> 142 143 <select class="hvn-theme-form-control" id="hvn-header-bedrooms" name="bedrooms"> 144 145 <?php foreach ( $count_options as $value => $label ) : ?> 146 147 <option value="<?php echo esc_attr( $value ); ?>"><?php echo esc_html( $label ); ?></option> 148 149 <?php endforeach; ?> 150 151 </select> 152 153 </div> 154 155 156 157 <div class="hvn-theme-property-search-panel__field"> 158 159 <label for="hvn-header-bathrooms"><?php esc_html_e( 'Bathrooms', 'havenlytics-realty' ); ?></label> 160 161 <select class="hvn-theme-form-control" id="hvn-header-bathrooms" name="bathrooms"> 162 163 <?php foreach ( $count_options as $value => $label ) : ?> 164 165 <option value="<?php echo esc_attr( $value ); ?>"><?php echo esc_html( $label ); ?></option> 166 167 <?php endforeach; ?> 168 169 </select> 170 171 </div> 172 173 </div> 174 175 176 177 <div class="hvn-theme-property-search-panel__row"> 178 179 <div class="hvn-theme-property-search-panel__field"> 180 181 <label for="hvn-header-min-price"><?php esc_html_e( 'Min price', 'havenlytics-realty' ); ?></label> 182 183 <input 184 185 type="number" 186 187 class="hvn-theme-form-control" 188 189 id="hvn-header-min-price" 190 191 name="min_price" 192 193 min="0" 194 195 step="1" 196 197 inputmode="numeric" 198 199 placeholder="<?php esc_attr_e( 'No min', 'havenlytics-realty' ); ?>" 200 201 /> 202 203 </div> 204 205 206 207 <div class="hvn-theme-property-search-panel__field"> 208 209 <label for="hvn-header-max-price"><?php esc_html_e( 'Max price', 'havenlytics-realty' ); ?></label> 210 211 <input 212 213 type="number" 214 215 class="hvn-theme-form-control" 216 217 id="hvn-header-max-price" 218 219 name="max_price" 220 221 min="0" 222 223 step="1" 224 225 inputmode="numeric" 226 227 placeholder="<?php esc_attr_e( 'No max', 'havenlytics-realty' ); ?>" 228 229 /> 230 231 </div> 232 233 </div> 234 235 67 236 68 237 <input type="hidden" name="view_type" value="grid" /> 238 69 239 <input type="hidden" name="orderby" value="date" /> 240 70 241 <input type="hidden" name="paged" value="1" /> 71 242 243 244 72 245 <div class="hvn-theme-property-search-panel__actions"> 246 73 247 <button type="submit" class="hvn-theme-btn hvn-theme-btn-primary hvn-theme-property-search-panel__submit"> 248 74 249 <?php esc_html_e( 'Search properties', 'havenlytics-realty' ); ?> 250 75 251 </button> 76 </div> 252 253 </div> 254 77 255 </form> 256 78 257 </div> 258 79 259 </div> 260 80 261 </div> 262 263 -
havenlytics-realty/1.15.5/template-parts/home/department-tabs.php
r334339 r334420 19 19 $columns = 3; 20 20 21 $search_url = hvn_realty_get_property_search_url(); 21 $view_all_url = hvn_realty_get_home_department_button_url(); 22 $view_all_text = hvn_realty_get_home_department_button_text(); 22 23 23 24 $departments = hvn_realty_get_property_departments(); … … 212 213 <footer class="hvn-realty-section__footer"> 213 214 214 <a class="hvn-realty-btn hvn-realty-btn--outline" href="<?php echo esc_url( $search_url ); ?>">215 216 <?php e sc_html_e( 'View All Properties', 'havenlytics-realty'); ?>215 <a id="hvn-realty-dept-view-all" class="hvn-realty-btn hvn-realty-btn--outline" href="<?php echo esc_url( $view_all_url ); ?>"> 216 217 <?php echo esc_html( $view_all_text ); ?> 217 218 218 219 </a> -
havenlytics-realty/1.15.5/templates/template-realty-home.php
r334339 r334420 11 11 12 12 <main id="primary" class="hvn-realty-home"> 13 <?php hvn_realty_render_homepage_sections(); ?> 13 <?php 14 if ( function_exists( 'hvn_realty_render_homepage_sections' ) ) { 15 hvn_realty_render_homepage_sections(); 16 } 17 ?> 14 18 </main> 15 19
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)